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

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