/* Copyright 1990,1991,1992 Eric R. Smith. All rights reserved. */ /* DOS directory functions */ #include "mint.h" /* change to a new drive: should always return a map of valid drives */ long d_setdrv(d) int d; { long r; extern long dosdrvs; /* in filesys.c */ r = drvmap() | dosdrvs | PSEUDODRVS; TRACE("Dsetdrv(%d)", d); if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) { DEBUG("Dsetdrv: invalid drive %d", d); return r; } curproc->base->p_defdrv = curproc->curdrv = d; return r; } long d_getdrv() { TRACE("Dgetdrv"); return curproc->curdrv; } long d_free(buf, d) long *buf; int d; { fcookie *dir; TRACE("Dfree(%d)", d); /* drive 0 means current drive, otherwise it's d-1 */ if (d) d = d-1; else d = curproc->curdrv; if (d < 0 || d >= NUM_DRIVES) return EDRIVE; /* check for a media change -- we don't care much either way, but it * does keep the results more accurate */ (void)disk_changed(d); /* use current directory, not root, since it's more likely that * programs are interested in the latter (this makes U: work much * better) */ dir = &curproc->curdir[d]; if (!dir->fs) { DEBUG("Dfree: bad drive"); return EDRIVE; } return (*dir->fs->dfree)(dir, buf); } /* temp1 is a convenient place for path2fs puts the last component of * the path name */ extern char temp1[PATH_MAX]; /* in filesys.c */ long d_create(path) const char *path; { fcookie dir; long r; TRACE("Dcreate(%s)", path); r = path2cookie(path, temp1, &dir); if (r) { DEBUG("Dcreate(%s): returning %ld", path, r); return r; /* an error occured */ } /* check for write permission on the directory */ r = dir_access(&dir, S_IWOTH); if (r) { DEBUG("Dcreate(%s): access to directory denied",path); return r; } return (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask); } long d_delete(path) const char *path; { fcookie parentdir, targdir; long r; PROC *p; int i; XATTR xattr; TRACE("Ddelete(%s)", path); r = path2cookie(path, temp1, &parentdir); if (r) { DEBUG("Ddelete(%s): error %lx", path, r); return r; } /* check for write permission on the directory which the target * is located */ if (r = dir_access(&parentdir, S_IWOTH)) { DEBUG("Ddelete(%s): access to directory denied", path); return r; } /* now get the info on the file itself */ r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0); if (r || (r = (*targdir.fs->getxattr)(&targdir, &xattr))) { DEBUG("Ddelete: error %ld on %s", r, path); return r; } /* if the "directory" is a symbolic link, really unlink it */ if ( (xattr.mode & S_IFMT) == S_IFLNK ) { return (*parentdir.fs->remove)(&parentdir, temp1); } if ( (xattr.mode & S_IFMT) != S_IFDIR ) { DEBUG("Ddelete: %s is not a directory", path); return EPTHNF; } /* don't delete anyone else's root or current directory */ for (p = proclist; p; p = p->gl_next) { if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q) continue; for (i = 0; i < NUM_DRIVES; i++) { if (samefile(&targdir, &p->root[i])) { DEBUG("Ddelete: directory %s is a root directory", path); return EACCDN; } else if (samefile(&targdir, &p->curdir[i])) { if (i == p->curdrv && p != curproc) DEBUG("Ddelete: directory %s is in use", path); else { p->curdir[i] = p->root[i]; } } } } return (*parentdir.fs->rmdir)(&parentdir, temp1); } long d_setpath(path) const char *path; { fcookie dir; int drv = curproc->curdrv; int i; char c; long r; XATTR xattr; TRACE("Dsetpath(%s)", path); r = path2cookie(path, follow_links, &dir); if (r) { DEBUG("Dsetpath(%s): returning %ld", path, r); return r; } if (path[0] && path[1] == ':') { c = *path; if (c >= 'a' && c <= 'z') drv = c-'a'; else if (c >= 'A' && c <= 'Z') drv = c-'A'; } r = (*dir.fs->getxattr)(&dir, &xattr); if (r < 0) { DEBUG("Dsetpath: file '%s': attributes not found", path); return r; } if (!(xattr.attr & FA_DIR)) { DEBUG("Dsetpath(%s): not a directory",path); return EPTHNF; } /* * watch out for symbolic links; if c:\foo is a link to d:\bar, then * "cd c:\foo" should also change the drive to d: */ if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) { for (i = 0; i < NUM_DRIVES; i++) { if (curproc->root[i].dev == dir.dev && curproc->root[i].fs == dir.fs) { if (drv == curproc->curdrv) curproc->curdrv = i; drv = i; break; } } } curproc->curdir[drv] = dir; return 0; } long d_getpath(path, drv) char *path; int drv; { fcookie *dir, *root; TRACE("Dgetpath(%c)", drv + '@'); if (drv < 0 || drv > NUM_DRIVES) return EDRIVE; drv = (drv == 0) ? curproc->curdrv : drv-1; root = &curproc->root[drv]; if (!root->fs) { /* maybe not initialized yet? */ changedrv(drv); root = &curproc->curdir[drv]; if (!root->fs) return EDRIVE; } dir = &curproc->curdir[drv]; return (*root->fs->getname)(root, dir, path); } long f_setdta(dta) DTABUF *dta; { TRACE("Fsetdta: %lx", dta); curproc->dta = dta; curproc->base->p_dta = (char *)dta; return 0; } long f_getdta() { long r; r = (long)curproc->dta; TRACE("Fgetdta: returning %lx", r); return r; } /* * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir. */ long f_sfirst(path, attrib) const char *path; int attrib; { char *s, *slash; FILESYS *fs; fcookie dir, newdir; DTABUF *dta; DIR *dirh; XATTR xattr; long r; int i, havelabel; /* a special hack just for ksh0.4 -- it uses '/' in a search where * '\' was better. Note that normally we shouldn't go around changing * the strings that people send us! */ if (curproc->domain == DOM_MINT && !strncmp(path, "x:/*.", 5)) ((char *)path)[2] = '\\'; TRACE("Fsfirst(%s, %x)", path, attrib); r = path2cookie(path, temp1, &dir); if (r) { DEBUG("Fsfirst(%s): path2cookie returned %ld", path, r); return r; } /* * we need to split the last name (which may be a pattern) off from * the rest of the path, even if FS_KNOPARSE is true */ slash = 0; s = temp1; while (*s) { if (*s == '\\') slash = s; s++; } if (slash) { *slash++ = 0; /* slash now points to a name or pattern */ r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0); if (r) { DEBUG("Fsfirst(%s): lookup returned %ld", path, r); return r; } dir = newdir; } else { slash = temp1; } /* BUG? what if there really is an empty file name? */ if (!*slash) { DEBUG("Fsfirst: empty pattern"); return EFILNF; } fs = dir.fs; dta = curproc->dta; /* Now, see if we can find a DIR slot for the search. We use the following * heuristics to try to avoid destroying a slot: * (1) if the search doesn't use wildcards, don't bother with a slot * (2) if an existing slot was for the same DTA address, re-use it * (3) if there's a free slot, re-use it. Slots are freed when the * corresponding search is terminated. */ for (i = 0; i < NUM_SEARCH; i++) { if (curproc->srchdta[i] == dta) { dirh = &curproc->srchdir[i]; if (dirh->fc.fs) { (*dirh->fc.fs->closedir)(dirh); dirh->fc.fs = 0; } curproc->srchdta[i] = 0; /* slot is now free */ } } /* copy the pattern over into dta_pat into TOS 8.3 form */ /* remember that "slash" now points at the pattern (it follows the last \, if any) */ copy8_3(dta->dta_pat, slash); /* if attrib & FA_LABEL, read the volume label */ /* BUG: the label date and time are wrong. Does it matter? */ havelabel = 0; if (attrib & FA_LABEL) { r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN); dta->dta_attrib = FA_LABEL; dta->dta_time = dta->dta_date = 0; dta->dta_size = 0; dta->magic = EVALID; if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat)) r = EFILNF; if (attrib == FA_LABEL) return r; else if (r == 0) havelabel = 1; } if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */ r = relpath2cookie(&dir, slash, follow_links, &newdir, 0); if (r == 0) { r = (*newdir.fs->getxattr)(&newdir, &xattr); } if (r) { DEBUG("Fsfirst(%s): couldn't get file attributes",path); return r; } dta->magic = EVALID; dta->dta_attrib = xattr.attr; dta->dta_time = xattr.mtime; dta->dta_date = xattr.mdate; dta->dta_size = xattr.size; strncpy(dta->dta_name, slash, TOS_NAMELEN-1); dta->dta_name[TOS_NAMELEN-1] = 0; if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) strupr(dta->dta_name); return 0; } /* There is a wild card. Try to find a slot for an opendir/readdir * search. NOTE: we also come here if we were asked to search for * volume labels and found one. */ for (i = 0; i < NUM_SEARCH; i++) { if (curproc->srchdta[i] == 0) break; } if (i == NUM_SEARCH) { int oldest = 0; int oldtime = curproc->srchtim[0]; DEBUG("Fsfirst(%s): having to re-use a directory slot!",path); for (i = 1; i < NUM_SEARCH; i++) { if (curproc->srchtim[i] < oldtime) { oldest = i; oldtime = curproc->srchtim[i]; } } /* OK, close this directory for re-use */ i = oldest; dirh = &curproc->srchdir[i]; if (dirh->fc.fs) { (*dirh->fc.fs->closedir)(dirh); dirh->fc.fs = 0; } curproc->srchdta[i] = 0; } /* check to see if we have read permission on the directory (and make * sure that it really is a directory! */ r = dir_access(&dir, S_IROTH); if (r) { DEBUG("Fsfirst(%s): access to directory denied",path); return r; } /* set up the directory for a search */ dirh = &curproc->srchdir[i]; dirh->fc = dir; dirh->index = 0; dirh->flags = TOS_SEARCH; r = (*dir.fs->opendir)(dirh, dirh->flags); if (r != 0) { DEBUG("Fsfirst(%s): couldn't open directory (error %ld)", path, r); return r; } /* mark the slot as in-use */ curproc->srchdta[i] = dta; /* set up the DTA for Fsnext */ dta->index = i; dta->magic = SVALID; dta->dta_sattrib = attrib; /* OK, now basically just do Fsnext, except that instead of ENMFIL we * return EFILNF. * NOTE: If we already have found a volume label from the search above, * then we skip the f_snext and just return that. */ if (havelabel) return 0; r = f_snext(); if (r == ENMFIL) r = EFILNF; if (r) TRACE("Fsfirst: returning %ld", r); return r; } /* * Counter for Fsfirst/Fsnext, so that we know which search slots are * least recently used. This is updated once per second by the code * in timeout.c. * BUG: 1/second is pretty low granularity */ long searchtime; long f_snext() { static char buf[TOS_NAMELEN+1]; DTABUF *dta = curproc->dta; FILESYS *fs; fcookie fc; int i; DIR *dirh; long r; XATTR xattr; TRACE("Fsnext"); if (dta->magic == EVALID) { DEBUG("Fsnext: DTA marked a failing search"); return ENMFIL; } if (dta->magic != SVALID) { DEBUG("Fsnext: dta incorrectly set up"); return EINVFN; } i = dta->index; dirh = &curproc->srchdir[i]; curproc->srchtim[i] = searchtime; fs = dirh->fc.fs; if (!fs) /* oops -- the directory got closed somehow */ return EINTRN; /* BUG: f_snext and readdir should check for disk media changes */ for(;;) { r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc); if (r == ENAMETOOLONG) { DEBUG("Fsnext: name too long"); continue; /* TOS programs never see these names */ } if (r != 0) { baderror: (void)(*fs->closedir)(dirh); dirh->fc.fs = 0; curproc->srchdta[i] = 0; dta->magic = EVALID; if (r != ENMFIL) DEBUG("Fsnext: returning %ld", r); return r; } if (!pat_match(buf, dta->dta_pat)) continue; /* different patterns */ /* check for search attributes */ r = (*fc.fs->getxattr)(&fc, &xattr); if (r) { DEBUG("Fsnext: couldn't get file attributes"); goto baderror; } /* if the file is a symbolic link, try to find what it's linked to */ if ( (xattr.mode & S_IFMT) == S_IFLNK ) { char linkedto[PATH_MAX]; r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX); if (r == 0) { /* the "1" tells relpath2cookie that we read a link */ r = relpath2cookie(&dirh->fc, linkedto, follow_links, &fc, 1); if (r == 0) r = (*fc.fs->getxattr)(&fc, &xattr); } if (r) { DEBUG("Fsnext: couldn't follow link: error %ld", r); } } /* silly TOS rules for matching attributes */ if (xattr.attr == 0) break; if (xattr.attr & 0x21) break; if (dta->dta_sattrib & xattr.attr) break; } /* here, we have a match */ dta->dta_attrib = xattr.attr; dta->dta_time = xattr.mtime; dta->dta_date = xattr.mdate; dta->dta_size = xattr.size; strcpy(dta->dta_name, buf); if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) { strupr(dta->dta_name); } return 0; } long f_attrib(name, rwflag, attr) const char *name; int rwflag; int attr; { fcookie fc; XATTR xattr; long r; TRACE("Fattrib(%s, %d)", name, attr); r = path2cookie(name, (char *)0, &fc); if (r) { DEBUG("Fattrib(%s): error %ld", name, r); return r; } r = (*fc.fs->getxattr)(&fc, &xattr); if (r) { DEBUG("Fattrib(%s): getxattr returned %ld", name, r); return r; } if (rwflag) { if (attr & (FA_LABEL|FA_DIR)) { DEBUG("Fattrib(%s): illegal attributes specified",name); return EACCDN; } else if (curproc->euid && curproc->euid != xattr.uid) { DEBUG("Fattrib(%s): not the file's owner",name); return EACCDN; } else if (xattr.attr & (FA_LABEL|FA_DIR)) { DEBUG("Fattrib(%s): file is a volume label " "or directory",name); return EACCDN; } return (*fc.fs->chattr)(&fc, attr); } else { return xattr.attr; } } long f_delete(name) const char *name; { fcookie dir; long r; TRACE("Fdelete(%s)", name); r = path2cookie(name, temp1, &dir); if (r) { DEBUG("Fdelete: error %ld", r); return r; } /* check for write permission on directory */ r = dir_access(&dir, S_IWOTH); if (r) { DEBUG("Fdelete(%s): write access to directory denied",name); return r; } /* BUG: we should check here for a read-only file */ return (*dir.fs->remove)(&dir,temp1); } long f_rename(junk, old, new) int junk; /* ignored, for TOS compatibility */ const char *old, *new; { fcookie olddir, newdir, oldfil; XATTR xattr; char temp2[PATH_MAX]; long r; TRACE("Frename(%s, %s)", old, new); r = path2cookie(old, temp2, &olddir); if (r) { DEBUG("Frename(%s,%s): error parsing old name",old,new); return r; } /* check for permissions on the old file * GEMDOS doesn't allow rename if the file is FA_RDONLY * we enforce this restriction only on regular files; processes, * directories, and character special files can be renamed at will */ r = relpath2cookie(&olddir, temp2, follow_links, &oldfil, 0); if (r) { DEBUG("Frename(%s,%s): old file not found",old,new); return r; } r = (*oldfil.fs->getxattr)(&oldfil, &xattr); if (r || ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) ) { DEBUG("Frename(%s,%s): access to old file not granted",old,new); return EACCDN; } r = path2cookie(new, temp1, &newdir); if (r) { DEBUG("Frename(%s,%s): error parsing new name",old,new); return r; } if (newdir.fs != olddir.fs) { DEBUG("Frename(%s,%s): different file systems",old,new); return EXDEV; /* cross device rename */ } /* check for write permission on both directories */ r = dir_access(&olddir, S_IWOTH); if (!r) r = dir_access(&newdir, S_IWOTH); if (r) { DEBUG("Frename(%s,%s): access to a directory denied",old,new); return r; } return (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1); } /* * GEMDOS extension: Dpathconf(name, which) * returns information about filesystem-imposed limits; "name" is the name * of a file or directory about which the limit information is requested; * "which" is the limit requested, as follows: * -1 max. value of "which" allowed * 0 internal limit on open files, if any * 1 max. number of links to a file {LINK_MAX} * 2 max. path name length {PATH_MAX} * 3 max. file name length {NAME_MAX} * 4 no. of bytes in atomic write to FIFO {PIPE_BUF} * 5 file name truncation rules * 6 file name case translation rules * * unlimited values are returned as 0x7fffffffL * * see also Sysconf() in dos.c */ long d_pathconf(name, which) const char *name; int which; { fcookie dir; long r; r = path2cookie(name, (char *)0, &dir); if (r) { DEBUG("Dpathconf(%s): bad path",name); return r; } r = (*dir.fs->pathconf)(&dir, which); if (which == DP_CASE && r == EINVFN) { /* backward compatibility with old .XFS files */ return (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS : DP_CASEINSENS; } return r; } /* * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new, * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for * arbitrary length file names */ long d_opendir(name, flag) const char *name; int flag; { DIR *dirh; fcookie dir; long r; r = path2cookie(name, follow_links, &dir); if (r) { DEBUG("Dopendir(%s): error %ld", name, r); return r; } r = dir_access(&dir, S_IROTH); if (r) { DEBUG("Dopendir(%s): read permission denied", name); return r; } dirh = (DIR *)umalloc(SIZEOF(DIR)); if (!dirh) return ENSMEM; dirh->fc = dir; dirh->index = 0; dirh->flags = flag; r = (*dir.fs->opendir)(dirh, flag); if (r) { DEBUG("d_opendir(%s): opendir returned %ld", name, r); ufree(dirh); return r; } return (long)dirh; } long d_readdir(len, handle, buf) int len; long handle; char *buf; { DIR *dirh = (DIR *)handle; fcookie fc; if (!dirh->fc.fs) return EIHNDL; return (*dirh->fc.fs->readdir)(dirh, buf, len, &fc); } long d_rewind(handle) long handle; { DIR *dirh = (DIR *)handle; if (!dirh->fc.fs) return EIHNDL; return (*dirh->fc.fs->rewinddir)(dirh); } long d_closedir(handle) long handle; { long r; DIR *dirh = (DIR *)handle; if (!dirh->fc.fs) return EIHNDL; r = (*dirh->fc.fs->closedir)(dirh); dirh->fc.fs = 0; if (r) { DEBUG("Dclosedir: error %ld", r); } ufree(dirh); return r; } /* * GEMDOS extension: Fxattr gets extended attributes for a file. "flag" * is 0 if symbolic links are to be followed (like stat), 1 if not (like * lstat). */ long f_xattr(flag, name, xattr) int flag; const char *name; XATTR *xattr; { fcookie fc; long r; TRACE("Fxattr(%d, %s)", flag, name); r = path2cookie(name, flag ? (char *)0 : follow_links, &fc); if (r) { DEBUG("Fxattr(%s): path2cookie returned %ld", name, r); return r; } r = (*fc.fs->getxattr)(&fc, xattr); if (r) { DEBUG("Fxattr(%s): returning %ld", name, r); } return r; } /* * GEMDOS extension: Flink(old, new) creates a hard link named "new" * to the file "old". */ long f_link(old, new) const char *old, *new; { fcookie olddir, newdir; char temp2[PATH_MAX]; long r; TRACE("Flink(%s, %s)", old, new); r = path2cookie(old, temp2, &olddir); if (r) { DEBUG("Flink(%s,%s): error parsing old name",old,new); return r; } r = path2cookie(new, temp1, &newdir); if (r) { DEBUG("Flink(%s,%s): error parsing new name",old,new); return r; } if (newdir.fs != olddir.fs) { DEBUG("Flink(%s,%s): different file systems",old,new); return EXDEV; /* cross device link */ } /* check for write permission on the destination directory */ r = dir_access(&newdir, S_IWOTH); if (r) { DEBUG("Flink(%s,%s): access to directory denied",old,new); return r; } return (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1); } /* * GEMDOS extension: Fsymlink(old, new): create a symbolic link named * "new" that contains the path "old". */ long f_symlink(old, new) const char *old, *new; { fcookie newdir; long r; TRACE("Fsymlink(%s, %s)", old, new); r = path2cookie(new, temp1, &newdir); if (r) { DEBUG("Fsymlink(%s,%s): error parsing %s", old,new,new); return r; } r = dir_access(&newdir, S_IWOTH); if (r) { DEBUG("Fsymlink(%s,%s): access to directory denied",old,new); return r; } return (*newdir.fs->symlink)(&newdir, temp1, old); } /* * GEMDOS extension: Freadlink(buflen, buf, linkfile): * read the contents of the symbolic link "linkfile" into the buffer * "buf", which has length "buflen". */ long f_readlink(buflen, buf, linkfile) int buflen; char *buf; const char *linkfile; { fcookie file; long r; XATTR xattr; TRACE("Freadlink(%s)", linkfile); r = path2cookie(linkfile, (char *)0, &file); if (r) { DEBUG("Freadlink: unable to find %s", linkfile); return r; } r = (*file.fs->getxattr)(&file, &xattr); if (r) { DEBUG("Freadlink: unable to get attributes for %s", linkfile); return r; } if ( (xattr.mode & S_IFMT) == S_IFLNK ) return (*file.fs->readlink)(&file, buf, buflen); DEBUG("Freadlink: %s is not a link", linkfile); return EACCDN; } /* * GEMDOS extension: Dcntl(): do file system specific functions */ long d_cntl(cmd, name, arg) int cmd; const char *name; long arg; { fcookie dir; long r; TRACE("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg); r = path2cookie(name, temp1, &dir); if (r) { DEBUG("Dcntl: couldn't find %s", name); return r; } return (*dir.fs->fscntl)(&dir, temp1, cmd, arg); } /* * GEMDOS extension: Fchown(name, uid, gid) changes the user and group * ownerships of a file to "uid" and "gid" respectively. */ long f_chown(name, uid, gid) const char *name; int uid, gid; { fcookie fc; XATTR xattr; long r; TRACE("Fchown(%s, %d, %d)", name, uid, gid); r = path2cookie(name, follow_links, &fc); if (r) { DEBUG("Fchown(%s): error %ld", name, r); return r; } /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can * only change the ownership of a file that is owned by this user, to * the effective group id of the process */ if (curproc->euid) { if (curproc->egid != gid) return EACCDN; r = (*fc.fs->getxattr)(&fc, &xattr); if (r) { DEBUG("Fchown(%s): unable to get file attributes",name); return r; } if (xattr.uid != curproc->euid || xattr.uid != uid) { DEBUG("Fchown(%s): not the file's owner",name); return EACCDN; } } return (*fc.fs->chown)(&fc, uid, gid); } /* * GEMDOS extension: Fchmod(file, mode) changes a file's access * permissions. */ long f_chmod(name, mode) const char *name; unsigned mode; { fcookie fc; long r; XATTR xattr; TRACE("Fchmod(%s, %o)", name, mode); r = path2cookie(name, follow_links, &fc); if (r) { DEBUG("Fchmod(%s): error %ld", name, r); return r; } r = (*fc.fs->getxattr)(&fc, &xattr); if (r) { DEBUG("Fchmod(%s): couldn't get file attributes",name); return r; } if (curproc->euid && curproc->euid != xattr.uid) { DEBUG("Fchmod(%s): not the file's owner",name); return EACCDN; } r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT); if (r) DEBUG("Fchmod: error %ld", r); return r; } /* * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to * a BIOS device. "mode" is 0 for unlock, 1 for lock; "dev" is a * BIOS device (0 for A:, 1 for B:, etc.). * Returns: 0 if the operation was successful * EACCDN if a lock attempt is made on a drive that is being * used * ELOCKED if the drive is locked by another process * ENSLOCK if a program attempts to unlock a drive it * hasn't locked. */ PROC *dlockproc[NUM_DRIVES]; long d_lock(mode, dev) int mode, dev; { PROC *p; FILEPTR *f; int i; DIR *dirh; TRACE("Dlock(%x,%c:)", mode, dev+'A'); if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE; if ( (mode&1) == 0) { /* unlock */ if (dlockproc[dev] == curproc) { dlockproc[dev] = 0; changedrv(dev); return 0; } DEBUG("Dlock: no such lock"); return ENSLOCK; } /* code for locking */ /* is the drive already locked? */ if (dlockproc[dev]) { DEBUG("Dlock: drive already locked"); return (dlockproc[dev] == curproc) ? 0 : ELOCKED; } /* see if the drive is in use */ for (p = proclist; p; p = p->gl_next) { if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q) continue; for (i = MIN_HANDLE; i < MAX_OPEN; i++) { if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) { DEBUG("Dlock: process %d has an open handle on the drive", p->pid); return EACCDN; } } for (i = 0; i < NUM_SEARCH; i++) { dirh = &curproc->srchdir[i]; if (dirh && dirh->fc.fs && dirh->fc.dev == dev) { DEBUG("Dlock: process %d has an open directory on the drive",p->pid); return EACCDN; } } } /* if we reach here, the drive is not in use */ /* we lock it by setting dlockproc and by setting all root and current * directories referring to the device to a null file system */ for (p = proclist; p; p = p->gl_next) { for (i = 0; i < NUM_DRIVES; i++) { if (p->root[i].dev == dev) p->root[i].fs = 0; if (p->curdir[i].dev == dev) p->curdir[i].fs = 0; } } dlockproc[dev] = curproc; return 0; }