home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
No Fragments Archive 10: Diskmags
/
nf_archive_10.iso
/
MAGS
/
PURE_B
/
PBMAG22A.MSA
/
MINT095S.ZIP
/
SRC
/
FILESYS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-04-22
|
23KB
|
950 lines
/*
Copyright 1990,1991,1992 Eric R. Smith. All rights reserved.
*/
/*
* various file system interface things
*/
#include "mint.h"
FILESYS *active_fs;
FILESYS *drives[NUM_DRIVES];
FILEPTR *flist; /* a list of free file pointers */
char follow_links[1]; /* dummy "name" used as a parameter to path2cookie */
/* vector of valid drives, according to GEMDOS */
/* note that this isn't necessarily the same as what the BIOS thinks of
* as valid
*/
long dosdrvs;
/*
* Initialize a specific drive. This is called whenever a new drive
* is accessed, or when media change occurs on an old drive.
* Assumption: at this point, active_fs is a valid pointer
* to a list of file systems.
*/
/* table of processes holding locks on drives */
extern PROC *dlockproc[]; /* in dosdir.c */
void
init_drive(i)
int i;
{
long r;
FILESYS *fs;
fcookie root_dir;
TRACE("init_drive(%c)", i+'A');
drives[i] = 0; /* no file system */
if (i >= 0 && i < NUM_DRIVES) {
if (dlockproc[i]) return;
}
for (fs = active_fs; fs; fs = fs->next) {
r = (*fs->root)(i, &root_dir);
if (r == 0) {
drives[i] = root_dir.fs;
break;
}
}
}
/*
* initialize the file system
*/
#define NUMFPS 40 /* initial number of file pointers */
void
init_filesys()
{
static FILEPTR initial[NUMFPS+1];
long drv;
int i;
extern FILESYS tos_filesys, bios_filesys, pipe_filesys,
proc_filesys, uni_filesys;
/* get the vector of connected GEMDOS drives */
dosdrvs = Dsetdrv(Dgetdrv()) | drvmap();
/* set up some initial file pointers */
for (i = 0; i < NUMFPS; i++) {
initial[i].devinfo = (ulong) (&initial[i+1]);
}
initial[NUMFPS].devinfo = 0;
flist = initial;
/* set up the file systems */
tos_filesys.next = 0;
bios_filesys.next = &tos_filesys;
pipe_filesys.next = &bios_filesys;
proc_filesys.next = &pipe_filesys;
uni_filesys.next = &proc_filesys;
active_fs = &uni_filesys;
/* initialize the BIOS file system */
biosfs_init();
/* initialize the unified file system */
unifs_init();
drv = dosdrvs | PSEUDODRVS;
#if 0
/* now run through the systems and see who's interested in what drives */
/* THIS IS NOW DONE AUTOMAGICALLY IN path2cookie */
for (i = 0; i < NUM_DRIVES; i++) {
if ( drv & (1L << i) )
init_drive(i);
}
#endif
}
/*
* load file systems from disk
* this routine is called after process 0 is set up, but before any user
* processes are run
*
* NOTE that a number of directory changes take place here: we look first
* in the current directory, then in the directory \mint, and finally
* the d_lock() calls force us into the root directory.
*/
typedef FILESYS * (*FSFUNC) P_((struct kerinfo *));
void
load_filesys()
{
long r;
BASEPAGE *b;
FILESYS *fs;
FSFUNC initf;
static DTABUF dta;
int i;
extern struct kerinfo kernelinfo; /* in main.c */
#define NPATHS 2
static const char *paths[NPATHS] = {"", "\\mint"};
curproc->dta = &dta;
for (i = 0; i < NPATHS; i++) {
if (*paths[i])
d_setpath(paths[i]);
r = f_sfirst("*.xfs", 0);
while (r == 0) {
b = (BASEPAGE *)p_exec(3, dta.dta_name, (char *)"", (char *)0);
if ( ((long)b) < 0 ) {
DEBUG("Error loading file system %s", dta.dta_name);
continue;
}
/* we leave a little bit of slop at the end of the loaded stuff */
m_shrink(0, (virtaddr)b, 512 + b->p_tlen + b->p_dlen + b->p_blen);
initf = (FSFUNC)b->p_tbase;
fs = (*initf)(&kernelinfo);
TRACE("initializing %s", dta.dta_name);
if (fs) {
TRACE("%s loaded OK", dta.dta_name);
fs->next = active_fs;
active_fs = fs;
} else {
DEBUG("%s returned null", dta.dta_name);
}
r = f_snext();
}
}
/* here, we invalidate all old drives EXCEPT for ones we're already using (at
* this point, only the bios devices should be open)
* this gives newly loaded file systems a chance to replace the
* default tosfs.c
*/
for (i = 0; i < NUM_DRIVES; i++) {
if (d_lock(1, i) == 0) /* lock if possible */
d_lock(0, i); /* and then unlock */
}
}
void
close_filesys()
{
PROC *p;
FILEPTR *f;
int i;
TRACE("close_filesys");
/* close every open file */
for (p = proclist; p; p = p->gl_next) {
for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
if ( (f = p->handle[i]) != 0) {
if (p->wait_q == TSR_Q || p->wait_q == ZOMBIE_Q)
ALERT("Open file for dead process?");
do_pclose(p, f);
}
}
}
}
/*
* "media change" routine: called when a media change is detected on device
* d, which may or may not be a BIOS device. All handles associated with
* the device are closed, and all directories invalidated. This routine
* does all the dirty work, and is called automatically when
* disk_changed detects a media change.
*/
void
changedrv(d)
unsigned d;
{
PROC *p;
int i;
FILEPTR *f;
FILESYS *fs;
DIR *dirh;
fcookie dir;
int warned = (d & 0xf000) == PROC_BASE_DEV;
int r;
/* re-initialize the device, if it was a BIOS device */
if (d < NUM_DRIVES) {
fs = drives[d];
if (fs) {
(void)(*fs->dskchng)(d);
}
init_drive(d);
}
for (p = proclist; p; p = p->gl_next) {
/* invalidate all open files on this device */
for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
if (((f = p->handle[i]) != 0) && (f->fc.dev == d)) {
if (!warned) {
ALERT(
"Files were open on a changed drive (0x%x)!", d);
warned++;
}
/* we set f->dev to NULL to indicate to do_pclose that this is an
* emergency close, and that it shouldn't try to make any
* calls to the device driver since the file has gone away
*/
f->dev = NULL;
(void)do_pclose(p, f);
p->handle[i] = 0;
}
}
/* terminate any active directory searches on the drive */
/* BUG: This handles only Fsfirst/Fsnext searches! */
for (i = 0; i < NUM_SEARCH; i++) {
dirh = &curproc->srchdir[i];
if (dirh->fc.fs && dirh->fc.dev == d) {
dirh->fc.fs = 0;
curproc->srchdta[i] = 0;
}
}
if (d >= NUM_DRIVES) continue;
/* change any active directories on the device to the (new) root */
fs = drives[d];
if (fs) {
r = (*fs->root)(d, &dir);
if (r != E_OK) dir.fs = 0;
} else {
dir.fs = 0; dir.dev = d;
}
for (i = 0; i < NUM_DRIVES; i++) {
if (p->root[i].dev == d)
p->root[i] = dir;
if (p->curdir[i].dev == d)
p->curdir[i] = dir;
}
}
}
/*
* check for media change: if the drive has changed, call changedrv to
* invalidate any open files and file handles associated with it, and
* call the file system's media change routine.
* returns: 0 if no change, 1 if change
*/
int
disk_changed(d)
int d;
{
short r;
FILESYS *fs;
static char tmpbuf[8192];
/* for now, only check BIOS devices */
if (d < 0 || d >= NUM_DRIVES)
return 0;
/* has the drive been initialized yet? If not, then initialize it and return
* "no change"
*/
if (!(fs = drives[d])) {
TRACE("drive %c not yet initialized", d+'A');
changedrv(d);
return 0;
}
/* We have to do this stuff no matter what, because someone may have installed
* vectors to force a media change...
* PROBLEM: AHDI may get upset if the drive isn't valid.
* SOLUTION: don't change the default PSEUDODRIVES setting!
*/
r = mediach(d);
if (r == 1) { /* drive _may_ have changed */
r = rwabs(0, tmpbuf, 1, 0, d, 0L); /* check the BIOS */
if (r != E_CHNG) { /* nope, no change */
return 0;
}
r = 2; /* drive was definitely changed */
}
if (r == 2) {
fs = drives[d]; /* get filesystem associated with drive */
if ((*fs->dskchng)(d)) { /* does the fs agree that it changed? */
changedrv(d); /* yes -- do the change */
return 1;
}
}
return 0;
}
/*
* routines for parsing path names
*/
char temp1[PATH_MAX]; /* temporary storage for file names */
#define DIRSEP(p) ((p) == '\\')
/*
* relpath2cookie converts a TOS file name into a file cookie representing
* the directory the file resides in, and a character string representing
* the name of the file in that directory. The character string is
* copied into the "lastname" array. If lastname is NULL, then the cookie
* returned actually represents the file, instead of just the directory
* the file is in.
*
* note that lastname, if non-null, should be big enough to contain all the
* characters in "path", since if the file system doesn't want the kernel
* to do path name parsing we may end up just copying path to lastname
* and returning the current or root directory, as appropriate
*
* "relto" is the directory relative to which the search should start.
* if you just want the current directory, use path2cookie instead.
*
* "depth" is used to control recursion in symbolic links; if it exceeds
* MAX_LINKS, we return ELOOP.
*
* N.B.: "depth" is also overloaded to control whether drive letter
* interpretation is performed; if drive == 0, it is assumed that
* drive letters should _not_ be interpreted; if drive > 0, it
* is assumed that they should be, since we are in this case following
* a symbolic link.
*/
#define MAX_LINKS 4
long
relpath2cookie(relto, path, lastname, res, depth)
fcookie *relto;
const char *path;
char *lastname;
fcookie *res;
int depth;
{
static fcookie dir;
int drv;
int len;
char c, *s;
XATTR xattr;
static char newpath[16] = "U:\\DEV\\";
char temp2[PATH_MAX];
char linkstuff[PATH_MAX];
long r = 0;
/* dolast: 0 == return a cookie for the directory the file is in
* 1 == return a cookie for the file itself, don't follow links
* 2 == return a cookie for whatever the file points at
*/
int dolast = 0;
int i = 0;
TRACE("relpath2cookie(%s)", path);
if (depth > MAX_LINKS) return ELOOP;
if (!lastname) {
dolast = 1;
lastname = temp2;
} else if (lastname == follow_links) {
dolast = 2;
lastname = temp2;
}
*lastname = 0;
/* special cases: CON:, AUX:, etc. should be converted to U:\DEV\CON,
* U:\DEV\AUX, etc.
*/
if (strlen(path) == 4 && path[3] == ':') {
strncpy(newpath+7, path, 3);
path = newpath;
}
/* first, check for a drive letter */
/* BUG: a '\' at the start of a symbolic link is relative to the current
* drive of the process, not the drive the link is located on
*/
if (path[1] == ':' && depth > 0) {
c = path[0];
if (c >= 'a' && c <= 'z')
drv = c - 'a';
else if (c >= 'A' && c <= 'Z')
drv = c - 'A';
else
goto nodrive;
path += 2;
i = 1; /* remember that we saw a drive letter */
} else {
nodrive:
drv = curproc->curdrv;
}
/* see if the path is rooted from '\\' */
if (DIRSEP(*path)) {
while(DIRSEP(*path))path++;
dir = curproc->root[drv];
} else {
if (i) { /* an explicit drive letter was given */
dir = curproc->curdir[drv];
}
else
dir = *relto;
}
if (!dir.fs) {
changedrv(dir.dev);
dir = curproc->root[drv];
}
if (!dir.fs) {
DEBUG("path2cookie: no file system");
return EDRIVE;
}
*res = dir;
if (!*path) { /* nothing more to do */
return 0;
}
/* here's where we come when we've gone across a mount point */
restart_mount:
/* see if there has been a disk change; if so, return E_CHNG.
* path2cookie will restart the search automatically; other functions
* that call relpath2cookie directly will have to fail gracefully
*/
if (disk_changed(dir.dev)) {
return E_CHNG;
}
if (dir.fs->fsflags & FS_KNOPARSE) {
if (!dolast) {
strncpy(lastname, path, PATH_MAX-1);
lastname[PATH_MAX - 1] = 0;
r = 0;
} else {
r = (*dir.fs->lookup)(&dir, path, res);
}
goto check_for_mount;
}
/* parse all but (possibly) the last component of the path name */
for(;;) {
/* if nothing left in path, and we don't care about links,
* then we're finished
*/
if (dolast < 2 && !*path) {
dir = *res;
break;
}
/* first, check to see if we're allowed to read this link/directory
* NOTE: at this point, "res" contains the new 'directory', and
* "dir" contains the old directory we were in (in case we need
* to call relpath2cookie on a link)
*/
r = (res->fs->getxattr)(res, &xattr);
if (r != 0) {
DEBUG("path2cookie: couldn't get file attributes");
break;
}
/* if the "directory" is a link, follow it */
i = depth;
while ( (xattr.mode & S_IFMT) == S_IFLNK ) {
if (i++ > MAX_LINKS)
return ELOOP;
r = (res->fs->readlink)(res, linkstuff, PATH_MAX);
if (r) {
DEBUG("error reading symbolic link");
break;
}
r = relpath2cookie(&dir, linkstuff, follow_links, res,
depth+1);
if (r) {
DEBUG("error following symbolic link");
break;
}
(void)(res->fs->getxattr)(res, &xattr);
}
/* if there's nothing left in the path, we can break here */
if (!*path) {
dir = *res;
break;
}
/* the "directory" had better, in fact, be a directory */
if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
return EPTHNF;
}
/* and we had better have search permission to it */
if (denyaccess(&xattr, S_IXOTH)) {
DEBUG("search permission in directory denied");
return EPTHNF;
}
dir = *res;
/* next, peel off the next name in the path */
len = 0;
s = lastname;
c = *path;
while (c && !DIRSEP(c)) {
if (len++ < PATH_MAX)
*s++ = c;
c = *++path;
}
*s = 0;
while(DIRSEP(*path))path++;
/* if there are no more names in the path, then we may be done */
if (dolast == 0 && !*path)
break;
r = (*dir.fs->lookup)(&dir, lastname, res);
if (r) { /* error? */
DEBUG("path2cookie: lookup returned %ld", r);
dir = *res;
break;
}
}
check_for_mount:
if (r == EMOUNT) { /* hmmm... a ".." at a mount point, maybe */
fcookie mounteddir;
r = (*dir.fs->root)(dir.dev, &mounteddir);
if (r == 0 && drv == UNIDRV) {
if (dir.fs == mounteddir.fs &&
dir.index == mounteddir.index &&
dir.dev == mounteddir.dev) {
*res = dir = curproc->root[UNIDRV];
TRACE("path2cookie: restarting from mount point");
goto restart_mount;
}
}
else r = 0;
}
return r;
}
#define MAX_TRYS 8
long
path2cookie(path, lastname, res)
const char *path;
char *lastname;
fcookie *res;
{
fcookie *dir;
long r;
/* AHDI sometimes will keep insisting that a media change occured;
* we limit the number or retrys to avoid hanging the system
*/
int trycnt = 0;
dir = &curproc->curdir[curproc->curdrv];
do {
/* NOTE: depth == 1 is necessary; see the comments before relpath2cookie */
r = relpath2cookie(dir, path, lastname, res, 1);
if (r == E_CHNG)
DEBUG("path2cookie: restarting due to media change");
} while (r == E_CHNG && trycnt++ < MAX_TRYS);
return r;
}
/*
* new_fileptr, dispose_fileptr: allocate (deallocate) a file pointer
*/
FILEPTR *
new_fileptr()
{
FILEPTR *f;
if ((f = flist)) {
flist = f->next;
f->next = 0;
return f;
}
f = kmalloc(SIZEOF(FILEPTR));
if (!f) {
FATAL("new_fileptr: out of memory");
}
else {
f->next = 0;
}
return f;
}
void
dispose_fileptr(f)
FILEPTR *f;
{
if (f->links != 0) {
FATAL("dispose_fileptr: f->links == %d", f->links);
}
f->next = flist;
flist = f;
}
/*
* denyshare(list, f): "list" points at the first FILEPTR in a
* chained list of open FILEPTRS referring to the same file;
* f is a newly opened FILEPTR. Every FILEPTR in the given list is
* checked to see if its "open" mode (in list->flags) is compatible with
* the open mode in f->flags. If not (for example, if f was opened with
* a "read" mode and some other file has the O_DENYREAD share mode),
* then 1 is returned. If all the open FILEPTRs in the list are
* compatible with f, then 0 is returned.
* This is not as complicated as it sounds. In practice, just keep a
* list of open FILEPTRs attached to each file, and put something like
* if (denyshare(thisfile->openfileptrlist, newfileptr))
* return EACCDN;
* in the device open routine.
*/
int
denyshare(list, f)
FILEPTR *list, *f;
{
int newrm, newsm;
int oldrm, oldsm;
int i;
newrm = f->flags & O_RWMODE;
newsm = f->flags & O_SHMODE;
for ( ; list; list = list->next) {
oldrm = list->flags & O_RWMODE;
oldsm = list->flags & O_SHMODE;
if (oldsm == O_DENYW || oldsm == O_DENYRW) {
if (newrm != O_RDONLY) {
DEBUG("write access denied");
return 1;
}
}
if (oldsm == O_DENYR || oldsm == O_DENYRW) {
if (newrm != O_WRONLY) {
DEBUG("read access denied");
return 1;
}
}
if (newsm == O_DENYW || newsm == O_DENYRW) {
if (oldrm != O_RDONLY) {
DEBUG("couldn't deny writes");
return 1;
}
}
if (newsm == O_DENYR || newsm == O_DENYRW) {
if (oldrm != O_WRONLY) {
DEBUG("couldn't deny reads");
return 1;
}
}
/* If either sm == O_COMPAT, then we check to make sure
that the file pointers are owned by the same process (O_COMPAT means
"deny access to any other processes"). Also, once a file is opened
in compatibility mode, it can't be opened in any other mode.
*/
if (newsm == O_COMPAT || oldsm == O_COMPAT) {
if (newsm != O_COMPAT || oldsm != O_COMPAT) {
DEBUG("O_COMPAT mode conflict");
return 1;
}
for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
if (curproc->handle[i] == list)
goto found;
}
/* old file pointer is not open by this process */
DEBUG("O_COMPAT file was opened by another process");
return 1;
found:
; /* everything is OK */
}
}
return 0;
}
/*
* denyaccess(XATTR *xattr, unsigned perm): checks to see if the access
* specified by perm (which must be some combination of S_IROTH, S_IWOTH,
* and S_IXOTH) should be granted to the current process
* on a file with the given extended attributes. Returns 0 if access
* by the current process is OK, 1 if not.
*/
int
denyaccess(xattr, perm)
XATTR *xattr;
unsigned perm;
{
unsigned mode;
/* the super-user can do anything! */
if (curproc->euid == 0)
return 0;
mode = xattr->mode;
if (curproc->euid == xattr->uid)
perm = perm << 6;
else if (curproc->egid == xattr->gid)
perm = perm << 3;
if ((mode & perm) != perm) return 1; /* access denied */
return 0;
}
/*
* Checks a lock against a list of locks to see if there is a conflict.
* This is a utility to be used by file systems, somewhat like denyshare
* above. Returns 0 if there is no conflict, or a pointer to the
* conflicting LOCK structure if there is.
*
* Conflicts occur for overlapping locks if the process id's are
* different and if at least one of the locks is a write lock.
*
* NOTE: we assume before being called that the locks have been converted
* so that l_start is absolute. not relative to the current position or
* end of file.
*/
LOCK *
denylock(list, lck)
LOCK *list, *lck;
{
LOCK *t;
unsigned long tstart, tend;
unsigned long lstart, lend;
int pid = curproc->pid;
int ltype;
ltype = lck->l.l_type;
lstart = lck->l.l_start;
if (lck->l.l_len == 0)
lend = 0xffffffff;
else
lend = lstart + lck->l.l_len;
for (t = list; t; t = t->next) {
tstart = t->l.l_start;
if (t->l.l_len == 0)
tend = 0xffffffff;
else
tend = tstart + t->l.l_len;
/* look for overlapping locks */
if (tstart <= lstart && tend >= lstart && t->l.l_pid != pid &&
(ltype == F_WRLCK || t->l.l_type == F_WRLCK))
break;
if (lstart <= tstart && lend >= tstart && t->l.l_pid != pid &&
(ltype == F_WRLCK || t->l.l_type == F_WRLCK))
break;
}
return t;
}
/*
* check to see that a file is a directory, and that write permission
* is granted; return an error code, or 0 if everything is ok.
*/
long
dir_access(dir, perm)
fcookie *dir;
unsigned perm;
{
XATTR xattr;
long r;
r = (*dir->fs->getxattr)(dir, &xattr);
if (r) return r;
if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
DEBUG("file is not a directory");
return EPTHNF;
}
if (denyaccess(&xattr, perm)) {
DEBUG("no permission for directory");
return EACCDN;
}
return 0;
}
/*
* returns 1 if the given name contains a wildcard character
*/
int
has_wild(name)
const char *name;
{
char c;
while (c = *name++) {
if (c == '*' || c == '?') return 1;
}
return 0;
}
/*
* void copy8_3(dest, src): convert a file name (src) into DOS 8.3 format
* (in dest). Note the following things:
* if a field has less than the required number of characters, it is
* padded with blanks
* a '*' means to pad the rest of the field with '?' characters
* special things to watch for:
* "." and ".." are more or less left alone
* "*.*" is recognized as a special pattern, for which dest is set
* to just "*"
* Long names are truncated. Any extensions after the first one are
* ignored, i.e. foo.bar.c -> foo.bar, foo.c.bar->foo.c.
*/
void
copy8_3(dest, src)
char *dest;
const char *src;
{
char fill = ' ', c;
int i;
if (src[0] == '.') {
if (src[1] == 0) {
strcpy(dest, ". . ");
return;
}
if (src[1] == '.' && src[2] == 0) {
strcpy(dest, ".. . ");
return;
}
}
if (src[0] == '*' && src[1] == '.' && src[2] == '*' && src[3] == 0) {
dest[0] = '*';
dest[1] = 0;
return;
}
for (i = 0; i < 8; i++) {
c = *src++;
if (!c || c == '.') break;
if (c == '*') {
fill = c = '?';
}
*dest++ = toupper(c);
}
while (i++ < 8) {
*dest++ = fill;
}
*dest++ = '.';
i = 0;
fill = ' ';
while (c && c != '.')
c = *src++;
if (c) {
for( ;i < 3; i++) {
c = *src++;
if (!c || c == '.') break;
if (c == '*')
c = fill = '?';
*dest++ = toupper(c);
}
}
while (i++ < 3)
*dest++ = fill;
*dest++ = 0;
}
/*
* int pat_match(name, patrn): returns 1 if "name" matches the template in
* "patrn", 0 if not. "patrn" is assumed to have been expanded in 8.3
* format by copy8_3; "name" need not be. Any '?' characters in patrn
* will match any character in name. Note that if "patrn" has a '*' as
* the first character, it will always match; this will happen only if
* the original pattern (before copy8_3 was applied) was "*.*".
*
* BUGS: acts a lot like the silly TOS pattern matcher.
*/
int
pat_match(name, template)
const char *name, *template;
{
register char *s, c;
char expname[TOS_NAMELEN+1];
if (*template == '*') return 1;
copy8_3(expname, name);
s = expname;
while (c = *template++) {
if (c != *s && c != '?')
return 0;
s++;
}
return 1;
}
/*
* int samefile(fcookie *a, fcookie *b): returns 1 if the two cookies
* refer to the same file or directory, 0 otherwise
*/
int
samefile(a, b)
fcookie *a, *b;
{
if (a->fs == b->fs && a->dev == b->dev && a->index == b->index)
return 1;
return 0;
}