home *** CD-ROM | disk | FTP | other *** search
/ Atari FTP / ATARI_FTP_0693.zip / ATARI_FTP_0693 / Mint / mint104s.zoo / mint.src / dosdir.c < prev    next >
C/C++ Source or Header  |  1993-03-08  |  30KB  |  1,314 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /* DOS directory functions */
  8.  
  9. #include "mint.h"
  10.  
  11. /* change to a new drive: should always return a map of valid drives */
  12.  
  13. long ARGS_ON_STACK
  14. d_setdrv(d)
  15.     int d;
  16. {
  17.     long r;
  18.     extern long dosdrvs;    /* in filesys.c */
  19.  
  20.     r = drvmap() | dosdrvs | PSEUDODRVS;
  21.  
  22.     TRACE(("Dsetdrv(%d)", d));
  23.     if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
  24.         DEBUG(("Dsetdrv: invalid drive %d", d));
  25.         return r;
  26.     }
  27.  
  28.     curproc->base->p_defdrv = curproc->curdrv = d;
  29.     return r;
  30. }
  31.  
  32.  
  33. long ARGS_ON_STACK
  34. d_getdrv()
  35. {
  36.     TRACE(("Dgetdrv"));
  37.     return curproc->curdrv;
  38. }
  39.  
  40. long ARGS_ON_STACK
  41. d_free(buf, d)
  42.     long *buf;
  43.     int d;
  44. {
  45.     fcookie *dir = 0;
  46.     extern int aliasdrv[];
  47.  
  48.     TRACE(("Dfree(%d)", d));
  49.  
  50. /* drive 0 means current drive, otherwise it's d-1 */
  51.     if (d)
  52.         d = d-1;
  53.     else
  54.         d = curproc->curdrv;
  55.  
  56. /* Hack to make programs (like df) which use drive
  57.  * information from Fxattr() work more often.
  58.  * BUG: this works only if the drive is a root,
  59.  * a current directory or an alias, or one of the
  60.  * standard drives.
  61.  */
  62.     if (d < 0 || d >= NUM_DRIVES) {
  63.         int i;
  64.  
  65.         for (i = 0; i < NUM_DRIVES; i++) {
  66.             if (aliasdrv[i] == d) {
  67.                 d = i;
  68.                 goto aliased;
  69.             }
  70.             if (curproc->curdir[i].dev == d) {
  71.                 dir = &curproc->curdir[i];
  72.             } else if (curproc->root[i].dev == d) {
  73.                 dir = &curproc->root[i];
  74.             }
  75.         }
  76.         if (dir && dir->fs) {
  77.             return (*dir->fs->dfree)(dir, buf);
  78.         }
  79.         return EDRIVE;
  80.     }
  81.  
  82. /* check for a media change -- we don't care much either way, but it
  83.  * does keep the results more accurate
  84.  */
  85.     (void)disk_changed(d);
  86.  
  87. aliased:
  88.  
  89. /* use current directory, not root, since it's more likely that
  90.  * programs are interested in the latter (this makes U: work much
  91.  * better)
  92.  */
  93.     dir = &curproc->curdir[d];
  94.     if (!dir->fs) {
  95.         DEBUG(("Dfree: bad drive"));
  96.         return EDRIVE;
  97.     }
  98.  
  99.     return (*dir->fs->dfree)(dir, buf);
  100. }
  101.  
  102. /* temp1 is a convenient place for path2fs puts the last component of
  103.  *   the path name
  104.  */
  105.  
  106. extern char temp1[PATH_MAX];    /* in filesys.c */
  107.  
  108. long ARGS_ON_STACK
  109. d_create(path)
  110.     const char *path;
  111. {
  112.     fcookie dir;
  113.     long r;
  114.  
  115.     TRACE(("Dcreate(%s)", path));
  116.  
  117.     r = path2cookie(path, temp1, &dir);
  118.     if (r) {
  119.         DEBUG(("Dcreate(%s): returning %ld", path, r));
  120.         return r;    /* an error occured */
  121.     }
  122. /* check for write permission on the directory */
  123.     r = dir_access(&dir, S_IWOTH);
  124.     if (r) {
  125.         DEBUG(("Dcreate(%s): access to directory denied",path));
  126.         release_cookie(&dir);
  127.         return r;
  128.     }
  129.     r = (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
  130.     release_cookie(&dir);
  131.     return r;
  132. }
  133.  
  134. long ARGS_ON_STACK
  135. d_delete(path)
  136.     const char *path;
  137. {
  138.     fcookie parentdir, targdir;
  139.     long r;
  140.     PROC *p;
  141.     int i;
  142.     XATTR xattr;
  143.  
  144.     TRACE(("Ddelete(%s)", path));
  145.  
  146.     r = path2cookie(path, temp1, &parentdir);
  147.  
  148.     if (r) {
  149.         DEBUG(("Ddelete(%s): error %lx", path, r));
  150.         release_cookie(&parentdir);
  151.         return r;
  152.     }
  153. /* check for write permission on the directory which the target
  154.  * is located
  155.  */
  156.     if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
  157.         DEBUG(("Ddelete(%s): access to directory denied", path));
  158.         release_cookie(&parentdir);
  159.         return r;
  160.     }
  161.  
  162. /* now get the info on the file itself */
  163.  
  164.     r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
  165.     if (r) {
  166. bailout:
  167.         release_cookie(&parentdir);
  168.         DEBUG(("Ddelete: error %ld on %s", r, path));
  169.         return r;
  170.     }
  171.     if ((r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
  172.         release_cookie(&targdir);
  173.         goto bailout;
  174.     }
  175.  
  176. /* if the "directory" is a symbolic link, really unlink it */
  177.     if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  178.         r = (*parentdir.fs->remove)(&parentdir, temp1);
  179.     } else if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
  180.         DEBUG(("Ddelete: %s is not a directory", path));
  181.         r = EPTHNF;
  182.     } else {
  183.  
  184. /* don't delete anyone else's root or current directory */
  185.         for (p = proclist; p; p = p->gl_next) {
  186.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  187.             continue;
  188.         for (i = 0; i < NUM_DRIVES; i++) {
  189.             if (samefile(&targdir, &p->root[i])) {
  190.                 DEBUG(("Ddelete: directory %s is a root directory",
  191.                     path));
  192. noaccess:
  193.                 release_cookie(&targdir);
  194.                 release_cookie(&parentdir);
  195.                 return EACCDN;
  196.             } else if (samefile(&targdir, &p->curdir[i])) {
  197.                 if (i == p->curdrv && p != curproc) {
  198.                     DEBUG(("Ddelete: directory %s is in use",
  199.                         path));
  200.                     goto noaccess;
  201.                 } else {
  202.                     release_cookie(&p->curdir[i]);
  203.                     dup_cookie(&p->curdir[i], &p->root[i]);
  204.                 } 
  205.             }
  206.         }
  207.         }
  208.         release_cookie(&targdir);
  209.         r = (*parentdir.fs->rmdir)(&parentdir, temp1);
  210.     }
  211.     release_cookie(&parentdir);
  212.     return r;
  213. }
  214.  
  215. long ARGS_ON_STACK
  216. d_setpath(path)
  217.     const char *path;
  218. {
  219.     fcookie dir;
  220.     int drv = curproc->curdrv;
  221.     int i;
  222.     char c;
  223.     long r;
  224.     XATTR xattr;
  225.  
  226.     TRACE(("Dsetpath(%s)", path));
  227.  
  228.     r = path2cookie(path, follow_links, &dir);
  229.  
  230.     if (r) {
  231.         DEBUG(("Dsetpath(%s): returning %ld", path, r));
  232.         return r;
  233.     }
  234.  
  235.     if (path[0] && path[1] == ':') {
  236.         c = *path;
  237.         if (c >= 'a' && c <= 'z')
  238.             drv = c-'a';
  239.         else if (c >= 'A' && c <= 'Z')
  240.             drv = c-'A';
  241.     }
  242.  
  243.     r = (*dir.fs->getxattr)(&dir, &xattr);
  244.  
  245.     if (r < 0) {
  246.         DEBUG(("Dsetpath: file '%s': attributes not found", path));
  247.         release_cookie(&dir);
  248.         return r;
  249.     }
  250.  
  251.     if (!(xattr.attr & FA_DIR)) {
  252.         DEBUG(("Dsetpath(%s): not a directory",path));
  253.         release_cookie(&dir);
  254.         return EPTHNF;
  255.     }
  256.  
  257. /*
  258.  * watch out for symbolic links; if c:\foo is a link to d:\bar, then
  259.  * "cd c:\foo" should also change the drive to d:
  260.  */
  261.     if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
  262.         for (i = 0; i < NUM_DRIVES; i++) {
  263.             if (curproc->root[i].dev == dir.dev &&
  264.                 curproc->root[i].fs == dir.fs) {
  265.                 if (drv == curproc->curdrv)
  266.                     curproc->curdrv = i;
  267.                 drv = i;
  268.                 break;
  269.             }
  270.         }
  271.     }
  272.     release_cookie(&curproc->curdir[drv]);
  273.     curproc->curdir[drv] = dir;
  274.     return 0;
  275. }
  276.  
  277. /* jr: like d_getpath, except that the caller provides a limit
  278.    for the max. number of characters to be put into the buffer.
  279.    Inspired by POSIX.1, getcwd(), 5.2.2 */
  280.  
  281. long ARGS_ON_STACK
  282. d_getcwd(path, drv, size)
  283.     char *path;
  284.     int drv, size;
  285. {
  286.     fcookie *dir, *root;
  287.     long r;
  288.     char buf[PATH_MAX];
  289.     FILESYS *fs;
  290.  
  291.     TRACE(("Dgetcwd(%c, %d)", drv + '@', size));
  292.     if (drv < 0 || drv > NUM_DRIVES)
  293.         return EDRIVE;
  294.  
  295.     drv = (drv == 0) ? curproc->curdrv : drv-1;
  296.  
  297.     root = &curproc->root[drv];
  298.  
  299.     if (!root->fs) {    /* maybe not initialized yet? */
  300.         changedrv(drv);
  301.         root = &curproc->curdir[drv];
  302.         if (!root->fs)
  303.             return EDRIVE;
  304.     }
  305.     fs = root->fs;
  306.     dir = &curproc->curdir[drv];
  307.  
  308.     if (!(fs->fsflags & FS_LONGPATH)) {
  309.         r = (*fs->getname)(root, dir, buf, PATH_MAX);
  310.         if (r) return r;
  311.         if (strlen(buf) < size) {
  312.             strcpy(path, buf);
  313.             return 0;
  314.         } else {
  315.             return ERANGE;
  316.         }
  317.     }
  318.     return (*fs->getname)(root, dir, path, size);
  319. }
  320.  
  321. long ARGS_ON_STACK
  322. d_getpath(path, drv)
  323.     char *path;
  324.     int drv;
  325. {
  326.     TRACE(("Dgetpath(%c)", drv + '@'));
  327.     return d_getcwd(path, drv, PATH_MAX);
  328. }
  329.  
  330. long ARGS_ON_STACK
  331. f_setdta(dta)
  332.     DTABUF *dta;
  333. {
  334.  
  335.     TRACE(("Fsetdta: %lx", dta));
  336.     curproc->dta = dta;
  337.     curproc->base->p_dta = (char *)dta;
  338.     return 0;
  339. }
  340.  
  341. long ARGS_ON_STACK
  342. f_getdta()
  343. {
  344.     long r;
  345.  
  346.     r = (long)curproc->dta;
  347.     TRACE(("Fgetdta: returning %lx", r));
  348.     return r;
  349. }
  350.  
  351. /*
  352.  * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
  353.  */
  354.  
  355. long ARGS_ON_STACK
  356. f_sfirst(path, attrib)
  357.     const char *path;
  358.     int attrib;
  359. {
  360.     char *s, *slash;
  361.     FILESYS *fs;
  362.     fcookie dir, newdir;
  363.     DTABUF *dta;
  364.     DIR *dirh;
  365.     XATTR xattr;
  366.     long r;
  367.     int i, havelabel;
  368.  
  369.     TRACE(("Fsfirst(%s, %x)", path, attrib));
  370.  
  371.     r = path2cookie(path, temp1, &dir);
  372.  
  373.     if (r) {
  374.         DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
  375.         return r;
  376.     }
  377.  
  378. /*
  379.  * we need to split the last name (which may be a pattern) off from
  380.  * the rest of the path, even if FS_KNOPARSE is true
  381.  */
  382.     slash = 0;
  383.     s = temp1;
  384.     while (*s) {
  385.         if (*s == '\\')
  386.             slash = s;
  387.         s++;
  388.     }
  389.  
  390.     if (slash) {
  391.         *slash++ = 0;    /* slash now points to a name or pattern */
  392.         r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
  393.         release_cookie(&dir);
  394.         if (r) {
  395.             DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
  396.             return r;
  397.         }
  398.         dir = newdir;
  399.     } else {
  400.         slash = temp1;
  401.     }
  402.  
  403. /* BUG? what if there really is an empty file name? */
  404.     if (!*slash) {
  405.         DEBUG(("Fsfirst: empty pattern"));
  406.         return EFILNF;
  407.     }
  408.  
  409.     fs = dir.fs;
  410.     dta = curproc->dta;
  411.  
  412. /* Now, see if we can find a DIR slot for the search. We use the following
  413.  * heuristics to try to avoid destroying a slot:
  414.  * (1) if the search doesn't use wildcards, don't bother with a slot
  415.  * (2) if an existing slot was for the same DTA address, re-use it
  416.  * (3) if there's a free slot, re-use it. Slots are freed when the
  417.  *     corresponding search is terminated.
  418.  */
  419.  
  420.     for (i = 0; i < NUM_SEARCH; i++) {
  421.         if (curproc->srchdta[i] == dta) {
  422.             dirh = &curproc->srchdir[i];
  423.             if (dirh->fc.fs) {
  424.                 (*dirh->fc.fs->closedir)(dirh);
  425.                 release_cookie(&dirh->fc);
  426.                 dirh->fc.fs = 0;
  427.             }
  428.             curproc->srchdta[i] = 0; /* slot is now free */
  429.         }
  430.     }
  431.  
  432. /* copy the pattern over into dta_pat into TOS 8.3 form */
  433. /* remember that "slash" now points at the pattern (it follows the last \,
  434.    if any)
  435.  */
  436.     copy8_3(dta->dta_pat, slash);
  437.  
  438. /* if attrib & FA_LABEL, read the volume label */
  439. /* BUG: the label date and time are wrong. Does it matter?
  440.  */
  441.     havelabel = 0;
  442.     if (attrib & FA_LABEL) {
  443.         r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN+1);
  444.         dta->dta_attrib = FA_LABEL;
  445.         dta->dta_time = dta->dta_date = 0;
  446.         dta->dta_size = 0;
  447.         dta->magic = EVALID;
  448.         if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
  449.             r = EFILNF;
  450.         if (attrib == FA_LABEL)
  451.             return r;
  452.         else if (r == 0)
  453.             havelabel = 1;
  454.     }
  455.  
  456.     if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
  457.         r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
  458.         if (r == 0) {
  459.             r = (*newdir.fs->getxattr)(&newdir, &xattr);
  460.             release_cookie(&newdir);
  461.         }
  462.         release_cookie(&dir);
  463.         if (r) {
  464.             DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
  465.             return r;
  466.         }
  467.         dta->magic = EVALID;
  468.         dta->dta_attrib = xattr.attr;
  469.         dta->dta_time = xattr.mtime;
  470.         dta->dta_date = xattr.mdate;
  471.         dta->dta_size = xattr.size;
  472.         strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
  473.         dta->dta_name[TOS_NAMELEN-1] = 0;
  474.         if (curproc->domain == DOM_TOS &&
  475.             !(fs->fsflags & FS_CASESENSITIVE))
  476.             strupr(dta->dta_name);
  477.         return 0;
  478.     }
  479.  
  480. /* There is a wild card. Try to find a slot for an opendir/readdir
  481.  * search. NOTE: we also come here if we were asked to search for
  482.  * volume labels and found one.
  483.  */
  484.     for (i = 0; i < NUM_SEARCH; i++) {
  485.         if (curproc->srchdta[i] == 0)
  486.             break;
  487.     }
  488.     if (i == NUM_SEARCH) {
  489.         int oldest = 0; long oldtime = curproc->srchtim[0];
  490.  
  491.         DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
  492.         for (i = 1; i < NUM_SEARCH; i++) {
  493.             if (curproc->srchtim[i] < oldtime) {
  494.                 oldest = i;
  495.                 oldtime = curproc->srchtim[i];
  496.             }
  497.         }
  498.     /* OK, close this directory for re-use */
  499.         i = oldest;
  500.         dirh = &curproc->srchdir[i];
  501.         if (dirh->fc.fs) {
  502.             (*dirh->fc.fs->closedir)(dirh);
  503.             release_cookie(&dirh->fc);
  504.             dirh->fc.fs = 0;
  505.         }
  506.         curproc->srchdta[i] = 0;
  507.     }
  508.  
  509. /* check to see if we have read permission on the directory (and make
  510.  * sure that it really is a directory!)
  511.  */
  512.     r = dir_access(&dir, S_IROTH);
  513.     if (r) {
  514.         DEBUG(("Fsfirst(%s): access to directory denied (error code %ld)", path, r));
  515.         release_cookie(&dir);
  516.         return r;
  517.     }
  518.  
  519. /* set up the directory for a search */
  520.     dirh = &curproc->srchdir[i];
  521.     dirh->fc = dir;
  522.     dirh->index = 0;
  523.     dirh->flags = TOS_SEARCH;
  524.     r = (*dir.fs->opendir)(dirh, dirh->flags);
  525.     if (r != 0) {
  526.         DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
  527.             path, r));
  528.         release_cookie(&dir);
  529.         return r;
  530.     }
  531.  
  532. /* mark the slot as in-use */
  533.     curproc->srchdta[i] = dta;
  534.  
  535. /* set up the DTA for Fsnext */
  536.     dta->index = i;
  537.     dta->magic = SVALID;
  538.     dta->dta_sattrib = attrib;
  539.  
  540. /* OK, now basically just do Fsnext, except that instead of ENMFIL we
  541.  * return EFILNF.
  542.  * NOTE: If we already have found a volume label from the search above,
  543.  * then we skip the f_snext and just return that.
  544.  */
  545.     if (havelabel)
  546.         return 0;
  547.  
  548.     r = f_snext();
  549.     if (r == ENMFIL) r = EFILNF;
  550.     if (r)
  551.         TRACE(("Fsfirst: returning %ld", r));
  552. /* release_cookie isn't necessary, since &dir is now stored in the
  553.  * DIRH structure and will be released when the search is completed
  554.  */
  555.     return r;
  556. }
  557.  
  558. /*
  559.  * Counter for Fsfirst/Fsnext, so that we know which search slots are
  560.  * least recently used. This is updated once per second by the code
  561.  * in timeout.c.
  562.  * BUG: 1/second is pretty low granularity
  563.  */
  564.  
  565. long searchtime;
  566.  
  567. long ARGS_ON_STACK
  568. f_snext()
  569. {
  570.     static char buf[TOS_NAMELEN+1];
  571.     DTABUF *dta = curproc->dta;
  572.     FILESYS *fs;
  573.     fcookie fc;
  574.     int i;
  575.     DIR *dirh;
  576.     long r;
  577.     XATTR xattr;
  578.  
  579.     TRACE(("Fsnext"));
  580.  
  581.     if (dta->magic == EVALID) {
  582.         DEBUG(("Fsnext: DTA marked a failing search"));
  583.         return ENMFIL;
  584.     }
  585.     if (dta->magic != SVALID) {
  586.         DEBUG(("Fsnext: dta incorrectly set up"));
  587.         return EINVFN;
  588.     }
  589.  
  590.     i = dta->index;
  591.     dirh = &curproc->srchdir[i];
  592.     curproc->srchtim[i] = searchtime;
  593.  
  594.     fs = dirh->fc.fs;
  595.     if (!fs)        /* oops -- the directory got closed somehow */
  596.         return EINTRN;
  597.  
  598. /* BUG: f_snext and readdir should check for disk media changes */
  599.  
  600.     for(;;) {
  601.         r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
  602.  
  603.         if (r == ENAMETOOLONG) {
  604.             DEBUG(("Fsnext: name too long"));
  605.             continue;    /* TOS programs never see these names */
  606.         }
  607.         if (r != 0) {
  608. baderror:
  609.             if (dirh->fc.fs)
  610.                 (void)(*fs->closedir)(dirh);
  611.             release_cookie(&dirh->fc);
  612.             dirh->fc.fs = 0;
  613.             curproc->srchdta[i] = 0;
  614.             dta->magic = EVALID;
  615.             if (r != ENMFIL)
  616.                 DEBUG(("Fsnext: returning %ld", r));
  617.             return r;
  618.         }
  619.  
  620.         if (!pat_match(buf, dta->dta_pat))
  621.         {
  622.             release_cookie(&fc);
  623.             continue;    /* different patterns */
  624.         }
  625.  
  626.     /* check for search attributes */
  627.         r = (*fc.fs->getxattr)(&fc, &xattr);
  628.         if (r) {
  629.             DEBUG(("Fsnext: couldn't get file attributes"));
  630.             release_cookie(&fc);
  631.             goto baderror;
  632.         }
  633.     /* if the file is a symbolic link, try to find what it's linked to */
  634.         if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  635.             char linkedto[PATH_MAX];
  636.             r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
  637.             release_cookie(&fc);
  638.             if (r == 0) {
  639.             /* the "1" tells relpath2cookie that we read a link */
  640.                 r = relpath2cookie(&dirh->fc, linkedto,
  641.                     follow_links, &fc, 1);
  642.                 if (r == 0) {
  643.                 r = (*fc.fs->getxattr)(&fc, &xattr);
  644.                 release_cookie(&fc);
  645.                 }
  646.             }
  647.             if (r) {
  648.                 DEBUG(("Fsnext: couldn't follow link: error %ld",
  649.                     r));
  650.             }
  651.         } else {
  652.             release_cookie(&fc);
  653.         }
  654.  
  655.     /* silly TOS rules for matching attributes */
  656.         if (xattr.attr == 0) break;
  657.         if (xattr.attr & 0x21) break;
  658.         if (dta->dta_sattrib & xattr.attr)
  659.             break;
  660.     }
  661.  
  662. /* here, we have a match */
  663.     dta->dta_attrib = xattr.attr;
  664.     dta->dta_time = xattr.mtime;
  665.     dta->dta_date = xattr.mdate;
  666.     dta->dta_size = xattr.size;
  667.     strcpy(dta->dta_name, buf);
  668.  
  669.     if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
  670.         strupr(dta->dta_name);
  671.     }
  672.     return 0;
  673. }
  674.  
  675. long ARGS_ON_STACK
  676. f_attrib(name, rwflag, attr)
  677.     const char *name;
  678.     int rwflag;
  679.     int attr;
  680. {
  681.     fcookie fc;
  682.     XATTR xattr;
  683.     long r;
  684.  
  685.     TRACE(("Fattrib(%s, %d)", name, attr));
  686.  
  687.     r = path2cookie(name, (char *)0, &fc);
  688.  
  689.     if (r) {
  690.         DEBUG(("Fattrib(%s): error %ld", name, r));
  691.         return r;
  692.     }
  693.  
  694.     r = (*fc.fs->getxattr)(&fc, &xattr);
  695.  
  696.     if (r) {
  697.         DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
  698.         release_cookie(&fc);
  699.         return r;
  700.     }
  701.  
  702.     if (rwflag) {
  703.         if (attr & (FA_LABEL|FA_DIR)) {
  704.             DEBUG(("Fattrib(%s): illegal attributes specified",name));
  705.             r = EACCDN;
  706.         } else if (curproc->euid && curproc->euid != xattr.uid) {
  707.             DEBUG(("Fattrib(%s): not the file's owner",name));
  708.             r = EACCDN;
  709.         } else if (xattr.attr & (FA_LABEL|FA_DIR)) {
  710.             DEBUG(("Fattrib(%s): file is a volume label "
  711.                   "or directory",name));
  712.             r = EACCDN;
  713.         } else {
  714.             r = (*fc.fs->chattr)(&fc, attr);
  715.         }
  716.         release_cookie(&fc);
  717.         return r;
  718.     } else {
  719.         release_cookie(&fc);
  720.         return xattr.attr;
  721.     }
  722. }
  723.  
  724. long ARGS_ON_STACK
  725. f_delete(name)
  726.     const char *name;
  727. {
  728.     fcookie dir;
  729.     long r;
  730.  
  731.     TRACE(("Fdelete(%s)", name));
  732.  
  733.     r = path2cookie(name, temp1, &dir);
  734.  
  735.     if (r) {
  736.         DEBUG(("Fdelete: error %ld", r));
  737.         return r;
  738.     }
  739.  
  740. /* check for write permission on directory */
  741.     r = dir_access(&dir, S_IWOTH);
  742.     if (r) {
  743.         DEBUG(("Fdelete(%s): write access to directory denied",name));
  744.     } else {
  745. /* BUG: we should check here for a read-only file */
  746.         r = (*dir.fs->remove)(&dir,temp1);
  747.     }
  748.     release_cookie(&dir);
  749.     return r;
  750. }
  751.  
  752. long ARGS_ON_STACK
  753. f_rename(junk, old, new)
  754.     int junk;        /* ignored, for TOS compatibility */
  755.     const char *old, *new;
  756. {
  757.     fcookie olddir, newdir, oldfil;
  758.     XATTR xattr;
  759.     char temp2[PATH_MAX];
  760.     long r;
  761.  
  762.     UNUSED(junk);
  763.  
  764.     TRACE(("Frename(%s, %s)", old, new));
  765.  
  766.     r = path2cookie(old, temp2, &olddir);
  767.     if (r) {
  768.         DEBUG(("Frename(%s,%s): error parsing old name",old,new));
  769.         return r;
  770.     }
  771. /* check for permissions on the old file
  772.  * GEMDOS doesn't allow rename if the file is FA_RDONLY
  773.  * we enforce this restriction only on regular files; processes,
  774.  * directories, and character special files can be renamed at will
  775.  */
  776.     r = relpath2cookie(&olddir, temp2, (char *)0, &oldfil, 0);
  777.     if (r) {
  778.         DEBUG(("Frename(%s,%s): old file not found",old,new));
  779.         release_cookie(&olddir);
  780.         return r;
  781.     }
  782.     r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
  783.     release_cookie(&oldfil);
  784.     if (r ||
  785.         ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
  786.     {
  787.         DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
  788.         release_cookie(&olddir);
  789.         return EACCDN;
  790.     }
  791.     r = path2cookie(new, temp1, &newdir);
  792.     if (r) {
  793.         DEBUG(("Frename(%s,%s): error parsing new name",old,new));
  794.         release_cookie(&olddir);
  795.         return r;
  796.     }
  797.  
  798.     if (newdir.fs != olddir.fs) {
  799.         DEBUG(("Frename(%s,%s): different file systems",old,new));
  800.         release_cookie(&olddir);
  801.         release_cookie(&newdir);
  802.         return EXDEV;    /* cross device rename */
  803.     }
  804.  
  805. /* check for write permission on both directories */
  806.     r = dir_access(&olddir, S_IWOTH);
  807.     if (!r) r = dir_access(&newdir, S_IWOTH);
  808.     if (r) {
  809.         DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
  810.     } else {
  811.         r = (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
  812.     }
  813.     release_cookie(&olddir);
  814.     release_cookie(&newdir);
  815.     return r;
  816. }
  817.  
  818. /*
  819.  * GEMDOS extension: Dpathconf(name, which)
  820.  * returns information about filesystem-imposed limits; "name" is the name
  821.  * of a file or directory about which the limit information is requested;
  822.  * "which" is the limit requested, as follows:
  823.  *    -1    max. value of "which" allowed
  824.  *    0    internal limit on open files, if any
  825.  *    1    max. number of links to a file    {LINK_MAX}
  826.  *    2    max. path name length        {PATH_MAX}
  827.  *    3    max. file name length        {NAME_MAX}
  828.  *    4    no. of bytes in atomic write to FIFO {PIPE_BUF}
  829.  *    5    file name truncation rules
  830.  *    6    file name case translation rules
  831.  *
  832.  * unlimited values are returned as 0x7fffffffL
  833.  *
  834.  * see also Sysconf() in dos.c
  835.  */
  836.  
  837. long ARGS_ON_STACK
  838. d_pathconf(name, which)
  839.     const char *name;
  840.     int which;
  841. {
  842.     fcookie dir;
  843.     long r;
  844.  
  845.     r = path2cookie(name, (char *)0, &dir);
  846.     if (r) {
  847.         DEBUG(("Dpathconf(%s): bad path",name));
  848.         return r;
  849.     }
  850.     r = (*dir.fs->pathconf)(&dir, which);
  851.     if (which == DP_CASE && r == EINVFN) {
  852.     /* backward compatibility with old .XFS files */
  853.         r = (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
  854.                 DP_CASEINSENS;
  855.     }
  856.     release_cookie(&dir);
  857.     return r;
  858. }
  859.  
  860. /*
  861.  * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
  862.  * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
  863.  * arbitrary length file names
  864.  */
  865.  
  866. long ARGS_ON_STACK
  867. d_opendir(name, flag)
  868.     const char *name;
  869.     int flag;
  870. {
  871.     DIR *dirh;
  872.     fcookie dir;
  873.     long r;
  874.  
  875.     r = path2cookie(name, follow_links, &dir);
  876.     if (r) {
  877.         DEBUG(("Dopendir(%s): error %ld", name, r));
  878.         return r;
  879.     }
  880.     r = dir_access(&dir, S_IROTH);
  881.     if (r) {
  882.         DEBUG(("Dopendir(%s): read permission denied", name));
  883.         release_cookie(&dir);
  884.         return r;
  885.     }
  886.  
  887.     dirh = (DIR *)kmalloc(SIZEOF(DIR));
  888.     if (!dirh) {
  889.         release_cookie(&dir);
  890.         return ENSMEM;
  891.     }
  892.  
  893.     dirh->fc = dir;
  894.     dirh->index = 0;
  895.     dirh->flags = flag;
  896.     r = (*dir.fs->opendir)(dirh, flag);
  897.     if (r) {
  898.         DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
  899.         release_cookie(&dir);
  900.         kfree(dirh);
  901.         return r;
  902.     }
  903.  
  904. /* we keep a chain of open directories so that if a process
  905.  * terminates without closing them all, we can clean up
  906.  */
  907.     dirh->next = curproc->searches;
  908.     curproc->searches = dirh;
  909.  
  910.     return (long)dirh;
  911. }
  912.  
  913. long ARGS_ON_STACK
  914. d_readdir(len, handle, buf)
  915.     int len;
  916.     long handle;
  917.     char *buf;
  918. {
  919.     DIR *dirh = (DIR *)handle;
  920.     fcookie fc;
  921.     long r;
  922.  
  923.     if (!dirh->fc.fs)
  924.         return EIHNDL;
  925.     r = (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
  926.     if (r == 0)
  927.         release_cookie(&fc);
  928.     return r;
  929. }
  930.  
  931. long ARGS_ON_STACK
  932. d_rewind(handle)
  933.     long handle;
  934. {
  935.     DIR *dirh = (DIR *)handle;
  936.  
  937.     if (!dirh->fc.fs)
  938.         return EIHNDL;
  939.     return (*dirh->fc.fs->rewinddir)(dirh);
  940. }
  941.  
  942. /*
  943.  * NOTE: there is also code in terminate() in dosmem.c that
  944.  * does automatic closes of directory searches.
  945.  * If you change d_closedir(), you may also need to change
  946.  * terminate().
  947.  */
  948.  
  949. long ARGS_ON_STACK
  950. d_closedir(handle)
  951.     long handle;
  952. {
  953.     long r;
  954.     DIR *dirh = (DIR *)handle;
  955.     DIR **where;
  956.  
  957.     if (!dirh->fc.fs)
  958.         return EIHNDL;
  959.     where = &curproc->searches;
  960.     while (*where && *where != dirh) {
  961.         where = &((*where)->next);
  962.     }
  963.     if (!*where) {
  964.         DEBUG(("Dclosedir: not an open directory"));
  965.         return EIHNDL;
  966.     }
  967.  
  968. /* unlink the directory from the chain */
  969.     *where = dirh->next;
  970.  
  971.     r = (*dirh->fc.fs->closedir)(dirh);
  972.     release_cookie(&dirh->fc);
  973.     dirh->fc.fs = 0;
  974.  
  975.     if (r) {
  976.         DEBUG(("Dclosedir: error %ld", r));
  977.     }
  978.     kfree(dirh);
  979.     return r;
  980. }
  981.  
  982. /*
  983.  * GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
  984.  * is 0 if symbolic links are to be followed (like stat), 1 if not (like
  985.  * lstat).
  986.  */
  987.  
  988. long ARGS_ON_STACK
  989. f_xattr(flag, name, xattr)
  990.     int flag;
  991.     const char *name;
  992.     XATTR *xattr;
  993. {
  994.     fcookie fc;
  995.     long r;
  996.  
  997.     TRACE(("Fxattr(%d, %s)", flag, name));
  998.  
  999.     r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
  1000.     if (r) {
  1001.         DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
  1002.         return r;
  1003.     }
  1004.     r = (*fc.fs->getxattr)(&fc, xattr);
  1005.     if (r) {
  1006.         DEBUG(("Fxattr(%s): returning %ld", name, r));
  1007.     }
  1008.     release_cookie(&fc);
  1009.     return r;
  1010. }
  1011.  
  1012. /*
  1013.  * GEMDOS extension: Flink(old, new) creates a hard link named "new"
  1014.  * to the file "old".
  1015.  */
  1016.  
  1017. long ARGS_ON_STACK
  1018. f_link(old, new)
  1019.     const char *old, *new;
  1020. {
  1021.     fcookie olddir, newdir;
  1022.     char temp2[PATH_MAX];
  1023.     long r;
  1024.  
  1025.     TRACE(("Flink(%s, %s)", old, new));
  1026.  
  1027.     r = path2cookie(old, temp2, &olddir);
  1028.     if (r) {
  1029.         DEBUG(("Flink(%s,%s): error parsing old name",old,new));
  1030.         return r;
  1031.     }
  1032.     r = path2cookie(new, temp1, &newdir);
  1033.     if (r) {
  1034.         DEBUG(("Flink(%s,%s): error parsing new name",old,new));
  1035.         release_cookie(&olddir);
  1036.         return r;
  1037.     }
  1038.  
  1039.     if (newdir.fs != olddir.fs) {
  1040.         DEBUG(("Flink(%s,%s): different file systems",old,new));
  1041.         release_cookie(&olddir);
  1042.         release_cookie(&newdir);
  1043.         return EXDEV;    /* cross device link */
  1044.     }
  1045.  
  1046. /* check for write permission on the destination directory */
  1047.  
  1048.     r = dir_access(&newdir, S_IWOTH);
  1049.     if (r) {
  1050.         DEBUG(("Flink(%s,%s): access to directory denied",old,new));
  1051.     } else
  1052.         r = (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
  1053.     release_cookie(&olddir);
  1054.     release_cookie(&newdir);
  1055.     return r;
  1056. }
  1057.  
  1058. /*
  1059.  * GEMDOS extension: Fsymlink(old, new): create a symbolic link named
  1060.  * "new" that contains the path "old".
  1061.  */
  1062.  
  1063. long ARGS_ON_STACK
  1064. f_symlink(old, new)
  1065.     const char *old, *new;
  1066. {
  1067.     fcookie newdir;
  1068.     long r;
  1069.  
  1070.     TRACE(("Fsymlink(%s, %s)", old, new));
  1071.  
  1072.     r = path2cookie(new, temp1, &newdir);
  1073.     if (r) {
  1074.         DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
  1075.         return r;
  1076.     }
  1077.     r = dir_access(&newdir, S_IWOTH);
  1078.     if (r) {
  1079.         DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
  1080.     } else
  1081.         r = (*newdir.fs->symlink)(&newdir, temp1, old);
  1082.     release_cookie(&newdir);
  1083.     return r;
  1084. }
  1085.  
  1086. /*
  1087.  * GEMDOS extension: Freadlink(buflen, buf, linkfile):
  1088.  * read the contents of the symbolic link "linkfile" into the buffer
  1089.  * "buf", which has length "buflen".
  1090.  */
  1091.  
  1092. long ARGS_ON_STACK
  1093. f_readlink(buflen, buf, linkfile)
  1094.     int buflen;
  1095.     char *buf;
  1096.     const char *linkfile;
  1097. {
  1098.     fcookie file;
  1099.     long r;
  1100.     XATTR xattr;
  1101.  
  1102.     TRACE(("Freadlink(%s)", linkfile));
  1103.  
  1104.     r = path2cookie(linkfile, (char *)0, &file);
  1105.     if (r) {
  1106.         DEBUG(("Freadlink: unable to find %s", linkfile));
  1107.         return r;
  1108.     }
  1109.     r = (*file.fs->getxattr)(&file, &xattr);
  1110.     if (r) {
  1111.         DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
  1112.     } else if ( (xattr.mode & S_IFMT) == S_IFLNK )
  1113.         r = (*file.fs->readlink)(&file, buf, buflen);
  1114.     else {
  1115.         DEBUG(("Freadlink: %s is not a link", linkfile));
  1116.         r = EACCDN;
  1117.     }
  1118.     release_cookie(&file);
  1119.     return r;
  1120. }
  1121.  
  1122. /*
  1123.  * GEMDOS extension: Dcntl(): do file system specific functions
  1124.  */
  1125.  
  1126. long ARGS_ON_STACK
  1127. d_cntl(cmd, name, arg)
  1128.     int cmd;
  1129.     const char *name;
  1130.     long arg;
  1131. {
  1132.     fcookie dir;
  1133.     long r;
  1134.  
  1135.     TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
  1136.  
  1137.     r = path2cookie(name, temp1, &dir);
  1138.     if (r) {
  1139.         DEBUG(("Dcntl: couldn't find %s", name));
  1140.         return r;
  1141.     }
  1142.     r = (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
  1143.     release_cookie(&dir);
  1144.     return r;
  1145. }
  1146.  
  1147. /*
  1148.  * GEMDOS extension: Fchown(name, uid, gid) changes the user and group
  1149.  * ownerships of a file to "uid" and "gid" respectively.
  1150.  */
  1151.  
  1152. long ARGS_ON_STACK
  1153. f_chown(name, uid, gid)
  1154.     const char *name;
  1155.     int uid, gid;
  1156. {
  1157.     fcookie fc;
  1158.     XATTR xattr;
  1159.     long r;
  1160.  
  1161.     TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
  1162.  
  1163.     r = path2cookie(name, follow_links, &fc);
  1164.     if (r) {
  1165.         DEBUG(("Fchown(%s): error %ld", name, r));
  1166.         return r;
  1167.     }
  1168.  
  1169. /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
  1170.  * only change the ownership of a file that is owned by this user, to
  1171.  * the effective group id of the process
  1172.  */
  1173.     if (curproc->euid) {
  1174.         if (curproc->egid != gid)
  1175.             r = EACCDN;
  1176.         else
  1177.             r = (*fc.fs->getxattr)(&fc, &xattr);
  1178.         if (r) {
  1179.             DEBUG(("Fchown(%s): unable to get file attributes",name));
  1180.             release_cookie(&fc);
  1181.             return r;
  1182.         }
  1183.         if (xattr.uid != curproc->euid || xattr.uid != uid) {
  1184.             DEBUG(("Fchown(%s): not the file's owner",name));
  1185.             release_cookie(&fc);
  1186.             return EACCDN;
  1187.         }
  1188.     }
  1189.     r = (*fc.fs->chown)(&fc, uid, gid);
  1190.     release_cookie(&fc);
  1191.     return r;
  1192. }
  1193.  
  1194. /*
  1195.  * GEMDOS extension: Fchmod(file, mode) changes a file's access
  1196.  * permissions.
  1197.  */
  1198.  
  1199. long ARGS_ON_STACK
  1200. f_chmod(name, mode)
  1201.     const char *name;
  1202.     unsigned mode;
  1203. {
  1204.     fcookie fc;
  1205.     long r;
  1206.     XATTR xattr;
  1207.  
  1208.     TRACE(("Fchmod(%s, %o)", name, mode));
  1209.     r = path2cookie(name, follow_links, &fc);
  1210.     if (r) {
  1211.         DEBUG(("Fchmod(%s): error %ld", name, r));
  1212.         return r;
  1213.     }
  1214.     r = (*fc.fs->getxattr)(&fc, &xattr);
  1215.     if (r) {
  1216.         DEBUG(("Fchmod(%s): couldn't get file attributes",name));
  1217.     }
  1218.     else if (curproc->euid && curproc->euid != xattr.uid) {
  1219.         DEBUG(("Fchmod(%s): not the file's owner",name));
  1220.         r = EACCDN;
  1221.     } else {
  1222.         r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
  1223.         if (r) DEBUG(("Fchmod: error %ld", r));
  1224.     }
  1225.     release_cookie(&fc);
  1226.     return r;
  1227. }
  1228.  
  1229. /*
  1230.  * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
  1231.  * a BIOS device. "mode" bit 0 is 0 for unlock, 1 for lock; "dev" is a
  1232.  * BIOS device (0 for A:, 1 for B:, etc.).
  1233.  *
  1234.  * Returns: 0 if the operation was successful
  1235.  *          EACCDN if a lock attempt is made on a drive that is being
  1236.  *            used
  1237.  *        ELOCKED if the drive is locked by another process
  1238.  *        ENSLOCK if a program attempts to unlock a drive it
  1239.  *            hasn't locked.
  1240.  * ++jr: if mode bit 1 is set, then instead of returning ELOCKED the
  1241.  * pid of the process which has locked the drive is returned (unless
  1242.  * it was locked by pid 0, in which case ELOCKED is still returned).
  1243.  */
  1244.  
  1245. PROC *dlockproc[NUM_DRIVES];
  1246.  
  1247. long ARGS_ON_STACK
  1248. d_lock(mode, dev)
  1249.     int mode, dev;
  1250. {
  1251.     PROC *p;
  1252.     FILEPTR *f;
  1253.     int i;
  1254.     extern int aliasdrv[];
  1255.  
  1256.     TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
  1257.     if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
  1258.     if (aliasdrv[dev]) {
  1259.         dev = aliasdrv[dev] - 1;
  1260.         if (dev < 0 || dev >= NUM_DRIVES)
  1261.             return EDRIVE;
  1262.     }
  1263.     if ( (mode&1) == 0) {    /* unlock */
  1264.         if (dlockproc[dev] == curproc) {
  1265.             dlockproc[dev] = 0;
  1266.             changedrv(dev);
  1267.             return 0;
  1268.         }
  1269.         DEBUG(("Dlock: no such lock"));
  1270.         return ENSLOCK;
  1271.     }
  1272.  
  1273. /* code for locking */
  1274. /* is the drive already locked? */
  1275.     if (dlockproc[dev]) {
  1276.         DEBUG(("Dlock: drive already locked"));
  1277.         if (dlockproc[dev] == curproc) return 0;
  1278.         if (dlockproc[dev]->pid == 0) return ELOCKED;
  1279.         return (mode & 2) ? dlockproc[dev]->pid : ELOCKED;
  1280.     }
  1281. /* see if the drive is in use */
  1282.     for (p = proclist; p; p = p->gl_next) {
  1283.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  1284.             continue;
  1285.         for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
  1286.             if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
  1287.         DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
  1288.                 if (p->pid == 0) return EACCDN;
  1289.                 return (mode & 2) ? p->pid : EACCDN;
  1290.             }
  1291.         }
  1292.     }
  1293.  
  1294. /* if we reach here, the drive is not in use */
  1295. /* we lock it by setting dlockproc and by setting all root and current
  1296.  * directories referring to the device to a null file system
  1297.  */
  1298.     for (p = proclist; p; p = p->gl_next) {
  1299.         for (i = 0; i < NUM_DRIVES; i++) {
  1300.             if (p->root[i].dev == dev) {
  1301.                 release_cookie(&p->root[i]);
  1302.                 p->root[i].fs = 0;
  1303.             }
  1304.             if (p->curdir[i].dev == dev) {
  1305.                 release_cookie(&p->curdir[i]);
  1306.                 p->curdir[i].fs = 0;
  1307.             }
  1308.         }
  1309.     }
  1310.  
  1311.     dlockproc[dev] = curproc;
  1312.     return 0;
  1313. }
  1314.