File:  [Atari MiNT] / MiNT / src / unifs.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:58:54 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

Copyright 1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

 */



/* a simple unified file system */



#include "mint.h"





extern FILESYS bios_filesys, proc_filesys, pipe_filesys, shm_filesys;



static long	ARGS_ON_STACK uni_root	P_((int drv, fcookie *fc));

static long	ARGS_ON_STACK uni_lookup	P_((fcookie *dir, const char *name, fcookie *fc));

static long	ARGS_ON_STACK uni_getxattr	P_((fcookie *fc, XATTR *xattr));

static long	ARGS_ON_STACK uni_chattr	P_((fcookie *fc, int attrib));

static long	ARGS_ON_STACK uni_chown	P_((fcookie *fc, int uid, int gid));

static long	ARGS_ON_STACK uni_chmode	P_((fcookie *fc, unsigned mode));

static long	ARGS_ON_STACK uni_rmdir	P_((fcookie *dir, const char *name));

static long	ARGS_ON_STACK uni_remove	P_((fcookie *dir, const char *name));

static long	ARGS_ON_STACK uni_getname	P_((fcookie *root, fcookie *dir,

						    char *pathname, int size));

static long	ARGS_ON_STACK uni_rename	P_((fcookie *olddir, char *oldname,

				    fcookie *newdir, const char *newname));

static long	ARGS_ON_STACK uni_opendir	P_((DIR *dirh, int flags));

static long	ARGS_ON_STACK uni_readdir	P_((DIR *dirh, char *nm, int nmlen, fcookie *));

static long	ARGS_ON_STACK uni_rewinddir	P_((DIR *dirh));

static long	ARGS_ON_STACK uni_closedir	P_((DIR *dirh));

static long	ARGS_ON_STACK uni_pathconf	P_((fcookie *dir, int which));

static long	ARGS_ON_STACK uni_dfree	P_((fcookie *dir, long *buf));

static DEVDRV *	ARGS_ON_STACK uni_getdev	P_((fcookie *fc, long *devsp));

static long	ARGS_ON_STACK uni_symlink	P_((fcookie *dir, const char *name, const char *to));

static long	ARGS_ON_STACK uni_readlink	P_((fcookie *fc, char *buf, int buflen));

static long	ARGS_ON_STACK uni_fscntl	P_((fcookie *dir, const char *name, int cmd, long arg));



FILESYS uni_filesys = {

	(FILESYS *)0,

	FS_LONGPATH,

	uni_root,

	uni_lookup, nocreat, uni_getdev, uni_getxattr,

	uni_chattr, uni_chown, uni_chmode,

	nomkdir, uni_rmdir, uni_remove, uni_getname, uni_rename,

	uni_opendir, uni_readdir, uni_rewinddir, uni_closedir,

	uni_pathconf, uni_dfree, nowritelabel, noreadlabel,

	uni_symlink, uni_readlink, nohardlink, uni_fscntl, nodskchng

};



/*

 * structure that holds files

 * if (mode & S_IFMT == S_IFDIR), then this is an alias for a drive:

 *	"dev" holds the appropriate BIOS device number, and

 *	"data" is meaningless

 * if (mode & S_IFMT == S_IFLNK), then this is a symbolic link:

 *	"dev" holds the user id of the owner, and

 *	"data" points to the actual link data

 */



typedef struct unifile {

	char name[NAME_MAX+1];

	ushort mode;

	ushort dev;

	FILESYS *fs;

	void *data;

	struct unifile *next;

	short cdate, ctime;

} UNIFILE;



static UNIFILE u_drvs[UNI_NUM_DRVS];

static UNIFILE *u_root = 0;



static long	do_ulookup	P_((fcookie *, const char *, fcookie *, UNIFILE **));



FILESYS *

get_filesys (dev)

     int dev;

{

  UNIFILE *u;



  for (u = u_root; u; u = u->next)

    if (u->dev == dev)

      return u->fs;

  return (FILESYS *) 0L;

}



void

unifs_init()

{

	UNIFILE *u = u_drvs;

	int i;



	u_root = u;

	for (i = 0; i < UNI_NUM_DRVS; i++,u++) {

		u->next = u+1;

		u->mode = S_IFDIR|DEFAULT_DIRMODE;

		u->dev = i;

		u->cdate = datestamp;

		u->ctime = timestamp;

		if (i == PROCDRV) {

			strcpy(u->name, "proc");

			u->fs = &proc_filesys;

		} else if (i == PIPEDRV) {

			strcpy(u->name, "pipe");

			u->fs = &pipe_filesys;

		} else if (i == BIOSDRV) {

			strcpy(u->name, "dev");

			u->fs = &bios_filesys;

		} else if (i == UNIDRV) {

			(u-1)->next = u->next;	/* skip this drive */

		} else if (i == SHMDRV) {

			strcpy(u->name, "shm");

			u->fs = &shm_filesys;

		} else {

			u->name[0] = i + 'a';

			u->name[1] = 0;

			u->fs = 0;

		}

	}

	--u;	/* oops, we went too far */

	u->next = 0;

}



static long ARGS_ON_STACK 

uni_root(drv, fc)

	int drv;

	fcookie *fc;

{

	if (drv == UNIDRV) {

		fc->fs = &uni_filesys;

		fc->dev = drv;

		fc->index = 0L;

		return 0;

	}

	fc->fs = 0;

	return EINTRN;

}



static long ARGS_ON_STACK 

uni_lookup(dir, name, fc)

	fcookie *dir;

	const char *name;

	fcookie *fc;

{

	return do_ulookup(dir, name, fc, (UNIFILE **)0);

}



/* worker function for uni_lookup; can also return the UNIFILE

 * pointer for the root directory

 */

static long

do_ulookup(dir, name, fc, up)

	fcookie *dir;

	const char *name;

	fcookie *fc;

	UNIFILE **up;

{

	UNIFILE *u;

	long drvs;

	FILESYS *fs;

	fcookie *tmp;

	extern long dosdrvs;



	TRACE(("uni_lookup(%s)", name));



	if (dir->index != 0) {

		DEBUG(("uni_lookup: bad directory"));

		return EPTHNF;

	}

/* special case: an empty name in a directory means that directory */

/* so do "." and ".." */



	if (!*name || !strcmp(name, ".") || !strcmp(name, "..")) {

		dup_cookie(fc, dir);

		return 0;

	}

	drvs = drvmap() | dosdrvs | PSEUDODRVS;

/*

 * OK, check the list of aliases and special directories

 */

	for (u = u_root; u; u = u->next) {

		if (!strnicmp(name, u->name, NAME_MAX)) {

			if ( (u->mode & S_IFMT) == S_IFDIR ) {

				if (u->dev >= NUM_DRIVES) {

					fs = u->fs;

					if (up) *up = u;

					return (*fs->root)(u->dev,fc);

				}

				if ((drvs & (1L << u->dev)) == 0)

					return EPTHNF;

				tmp = &curproc->root[u->dev];

				if (!tmp->fs) {		/* drive changed? */

					changedrv(tmp->dev);

					tmp = &curproc->root[u->dev];

					if (!tmp->fs)

						return EPTHNF;

				}

				dup_cookie(fc, tmp);

			} else {		/* a symbolic link */

				fc->fs = &uni_filesys;

				fc->dev = UNIDRV;

				fc->index = (long)u;

			}

			if (up) *up = u;

			return 0;

		}

	}

	DEBUG(("uni_lookup: name (%s) not found", name));

	return EFILNF;

}



static long ARGS_ON_STACK 

uni_getxattr(fc, xattr)

	fcookie *fc;

	XATTR *xattr;

{

	UNIFILE *u = (UNIFILE *)fc->index;



	if (fc->fs != &uni_filesys) {

		ALERT("ERROR: wrong file system getxattr called");

		return EINTRN;

	}



	xattr->index = fc->index;

	xattr->dev = xattr->rdev = fc->dev;

	xattr->nlink = 1;

	xattr->blksize = 1;



/* If "u" is null, then we have the root directory, otherwise

 * we use the UNIFILE structure to get the info about it

 */

	if (!u || ( (u->mode & S_IFMT) == S_IFDIR )) {

		xattr->uid = xattr->gid = 0;

		xattr->size = xattr->nblocks = 0;

		xattr->mode = S_IFDIR | DEFAULT_DIRMODE;

		xattr->attr = FA_DIR;

	} else {

		xattr->uid = u->dev;

		xattr->gid = 0;

		xattr->size = xattr->nblocks = strlen(u->data) + 1;

		xattr->mode = u->mode;

		xattr->attr = 0;

	}

	xattr->mtime = xattr->atime = xattr->ctime = u->ctime;

	xattr->mdate = xattr->adate = xattr->cdate = u->cdate;

	return 0;

}



static long ARGS_ON_STACK 

uni_chattr(dir, attrib)

	fcookie *dir;

	int attrib;

{

	UNUSED(dir); UNUSED(attrib);

	return EACCDN;

}



static long ARGS_ON_STACK 

uni_chown(dir, uid, gid)

	fcookie *dir;

	int uid, gid;

{

	UNUSED(dir); UNUSED(uid);

	UNUSED(gid);

	return EINVFN;

}



static long ARGS_ON_STACK 

uni_chmode(dir, mode)

	fcookie *dir;

	unsigned mode;

{

	UNUSED(dir);

	UNUSED(mode);

	return EINVFN;

}



static long ARGS_ON_STACK 

uni_rmdir(dir, name)

	fcookie *dir;

	const char *name;

{

	long r;



	r = uni_remove(dir, name);

	if (r == EFILNF) r = EPTHNF;

	return r;

}



static long ARGS_ON_STACK 

uni_remove(dir, name)

	fcookie *dir;

	const char *name;

{

	UNIFILE *u, *lastu;



	UNUSED(dir);



	lastu = 0;

	u = u_root;

	while (u) {

		if (!strnicmp(u->name, name, NAME_MAX)) {

			if ( (u->mode & S_IFMT) != S_IFLNK ) return EFILNF;

			if (curproc->euid && (u->dev != curproc->euid))

				return EACCDN;

			kfree(u->data);

			if (lastu)

				lastu->next = u->next;

			else

				u_root = u->next;

			kfree(u);

			return 0;

		}

		lastu = u;

		u = u->next;

	}

	return EFILNF;

}



static long ARGS_ON_STACK 

uni_getname(root, dir, pathname, size)

	fcookie *root, *dir; char *pathname;

	int size;

{

	FILESYS *fs;

	UNIFILE *u;

	char *n;

	fcookie relto;

	char tmppath[PATH_MAX];

	long r;



	UNUSED(root);



	if (size <= 0) return ERANGE;



	fs = dir->fs;

	if (dir->dev == UNIDRV) {

		*pathname = 0;

		return 0;

	}



	for (u = u_root; u; u = u->next) {

		if (dir->dev == u->dev && (u->mode & S_IFMT) == S_IFDIR) {

			*pathname++ = '\\';

			if (--size <= 0) return ERANGE;

			for (n = u->name; *n; ) {

				*pathname++ = *n++;

				if (--size <= 0) return ERANGE;

			}

			break;

		}

	}



	if (!u) {

		ALERT("unifs: couldn't match a drive with a directory");

		return EPTHNF;

	}



	if (dir->dev >= NUM_DRIVES) {

		if ((*fs->root)(dir->dev, &relto) == 0) {

			if (!(fs->fsflags & FS_LONGPATH)) {

				r = (*fs->getname)(&relto, dir, tmppath, PATH_MAX);

				release_cookie(&relto);

				if (r) {

					return r;

				}

				if (strlen(tmppath) < size) {

					strcpy(pathname, tmppath);

					return 0;

				} else {

					return ERANGE;

				}

			}

			r = (*fs->getname)(&relto, dir, pathname, size);

			release_cookie(&relto);

			return r;

		} else {

			*pathname = 0;

			return EINTRN;

		}

	}



	if (curproc->root[dir->dev].fs != fs) {

		ALERT("unifs: drive's file system doesn't match directory's");

		return EINTRN;

	}



	if (!fs) {

		*pathname = 0;

		return 0;

	}

	if (!(fs->fsflags & FS_LONGPATH)) {

		r = (*fs->getname)(&curproc->root[dir->dev], dir, tmppath, PATH_MAX);

		if (r) return r;

		if (strlen(tmppath) < size) {

			strcpy(pathname, tmppath);

			return 0;

		} else {

			return ERANGE;

		}

	}

	return (*fs->getname)(&curproc->root[dir->dev], dir, pathname, size);

}



static long ARGS_ON_STACK 

uni_rename(olddir, oldname, newdir, newname)

	fcookie *olddir;

	char *oldname;

	fcookie *newdir;

	const char *newname;

{

	UNIFILE *u;

	fcookie fc;

	long r;



	UNUSED(olddir);



	for (u = u_root; u; u = u->next) {

		if (!strnicmp(u->name, oldname, NAME_MAX))

			break;

	}



	if (!u) {

		DEBUG(("uni_rename: old file not found"));

		return EFILNF;

	}



/* the new name is not allowed to exist! */

	r = uni_lookup(newdir, newname, &fc);

	if (r == 0)

		release_cookie(&fc);



	if (r != EFILNF) {

		DEBUG(("uni_rename: error %ld", r));

		return (r == 0) ? EACCDN : r;

	}



	(void)strncpy(u->name, newname, NAME_MAX);

	return 0;

}



static long ARGS_ON_STACK 

uni_opendir(dirh, flags)

	DIR *dirh;

	int flags;

{

	UNUSED(flags);



	if (dirh->fc.index != 0) {

		DEBUG(("uni_opendir: bad directory"));

		return EPTHNF;

	}

	dirh->index = 0;

	return 0;

}





static long ARGS_ON_STACK 

uni_readdir(dirh, name, namelen, fc)

	DIR *dirh;

	char *name;

	int namelen;

	fcookie *fc;

{

	long map;

	char *dirname;

	int i;

	int giveindex = (dirh->flags == 0);

	UNIFILE *u;

	long index;

	extern long dosdrvs;

	long r;



	map = dosdrvs | drvmap() | PSEUDODRVS;

	i = dirh->index++;

	u = u_root;

	while (i > 0) {

		--i;

		u = u->next;

		if (!u)

			break;

	}

tryagain:

	if (!u) return ENMFIL;



	dirname = u->name;

	index = (long)u;

	if ( (u->mode & S_IFMT) == S_IFDIR ) {

/* make sure the drive really exists */

		if ( u->dev >= NUM_DRIVES) {

		    r = (*u->fs->root)(u->dev,fc);

		    if (r) {

			fc->fs = &uni_filesys;

			fc->index = 0;

			fc->dev = u->dev;

		    }

		} else {

		    if ((map & (1L << u->dev)) == 0 ) {

			dirh->index++;

			u = u->next;

			goto tryagain;

		    }

		    dup_cookie(fc, &curproc->root[u->dev]);

		    if (!fc->fs) {	/* drive not yet initialized */

		/* use default attributes */

			fc->fs = &uni_filesys;

			fc->index = 0;

			fc->dev = u->dev;

		    }

		}

	} else {		/* a symbolic link */

		fc->fs = &uni_filesys;

		fc->dev = UNIDRV;

		fc->index = (long)u;

	}



	if (giveindex) {

		namelen -= (int)sizeof(long);

		if (namelen <= 0) {

			release_cookie(fc);

			return ERANGE;

		}

		*((long *)name) = index;

		name += sizeof(long);

	}

	strncpy(name, dirname, namelen-1);

	if (strlen(name) < strlen(dirname)) {

		release_cookie(fc);

		return ENAMETOOLONG;

	}

	return 0;

}



static long ARGS_ON_STACK 

uni_rewinddir(dirh)

	DIR *dirh;

{

	dirh->index = 0;

	return 0;

}



static long ARGS_ON_STACK 

uni_closedir(dirh)

	DIR *dirh;

{

	UNUSED(dirh);

	return 0;

}



static long ARGS_ON_STACK 

uni_pathconf(dir, which)

	fcookie *dir;

	int which;

{

	UNUSED(dir);



	switch(which) {

	case -1:

		return DP_MAXREQ;

	case DP_IOPEN:

		return 0;		/* no files to open */

	case DP_MAXLINKS:

		return 1;		/* no hard links available */

	case DP_PATHMAX:

		return PATH_MAX;

	case DP_NAMEMAX:

		return NAME_MAX;

	case DP_ATOMIC:

		return 1;		/* no atomic writes */

	case DP_TRUNC:

		return DP_AUTOTRUNC;

	case DP_CASE:

		return DP_CASEINSENS;

	case DP_MODEATTR:

		return DP_FT_DIR|DP_FT_LNK;

	case DP_XATTRFIELDS:

		return DP_INDEX|DP_DEV|DP_NLINK|DP_SIZE;

	default:

		return EINVFN;

	}

}



static long ARGS_ON_STACK 

uni_dfree(dir, buf)

	fcookie *dir;

	long *buf;

{

	UNUSED(dir);



	buf[0] = 0;	/* number of free clusters */

	buf[1] = 0;	/* total number of clusters */

	buf[2] = 1;	/* sector size (bytes) */

	buf[3] = 1;	/* cluster size (sectors) */

	return 0;

}



static DEVDRV * ARGS_ON_STACK 

uni_getdev(fc, devsp)

	fcookie *fc;

	long *devsp;

{

	UNUSED(fc);



	*devsp = EACCDN;

	return 0;

}



static long ARGS_ON_STACK 

uni_symlink(dir, name, to)

	fcookie *dir;

	const char *name;

	const char *to;

{

	UNIFILE *u;

	fcookie fc;

	long r;



	r = uni_lookup(dir, name, &fc);

	if (r == 0) {

		release_cookie(&fc);

		return EACCDN;	/* file already exists */

	}

	if (r != EFILNF) return r;	/* some other error */



	if (curproc->egid)

		return EACCDN;	/* only members of admin group may do that */



	u = kmalloc(SIZEOF(UNIFILE));

	if (!u) return EACCDN;



	strncpy(u->name, name, NAME_MAX);

	u->name[NAME_MAX] = 0;



	u->data = kmalloc((long)strlen(to)+1);

	if (!u->data) {

		kfree(u);

		return EACCDN;

	}

	strcpy(u->data, to);

	u->mode = S_IFLNK | DEFAULT_DIRMODE;

	u->dev = curproc->euid;

	u->next = u_root;

	u->fs = &uni_filesys;

	u->cdate = datestamp;

	u->ctime = timestamp;

	u_root = u;

	return 0;

}



static long ARGS_ON_STACK 

uni_readlink(fc, buf, buflen)

	fcookie *fc;

	char *buf;

	int buflen;

{

	UNIFILE *u;



	u = (UNIFILE *)fc->index;

	assert(u);

	assert((u->mode & S_IFMT) == S_IFLNK);

	assert(u->data);

	strncpy(buf, u->data, buflen);

	if (strlen(u->data) >= buflen)

		return ENAMETOOLONG;

	return 0;

}









/* uk: use these Dcntl's to install a new filesystem which is only visible

 *     on drive u:

 *

 *     FS_INSTALL:   let the kernel know about the file system; it does NOT

 *                   get a device number.

 *     FS_MOUNT:     use Dcntl(FS_MOUNT, "u:\\foo", &descr) to make a directory

 *                   foo where the filesytem resides in; the file system now

 *                   gets its device number which is also written into the

 *                   dev_no field of the fs_descr structure.

 *     FS_UNMOUNT:   remove a file system's directory; this call closes all

 *                   open files, directory searches and directories on this

 *                   device. Make sure that the FS will not recognise any

 *                   accesses to this device, as fs->root will be called

 *                   during the reinitalisation!

 *     FS_UNINSTALL: remove a file system completely from the kernel list,

 *                   but that will only be possible if there is no directory

 *                   associated with this file system.

 *                   This function allows it to write file systems as demons

 *                   which stay in memory only as long as needed.

 *

 * BUG: it is not possible yet to lock such a filesystem.

 */



/* here we start with gemdos only file system device numbers */

static curr_dev_no = 0x100;







static long ARGS_ON_STACK

uni_fscntl(dir, name, cmd, arg)

	fcookie *dir;

	const char *name;

	int cmd;

	long arg;

{

	fcookie fc;

	long r;



	extern struct kerinfo kernelinfo;

	extern FILESYS *active_fs;



	if (cmd == (int)FS_INSTALL) { /* install a new filesystem */

		struct fs_descr *d = (struct fs_descr*)arg;

		FILESYS *fs;



	/* check if FS is installed already */

		for (fs = active_fs;  fs;  fs = fs->next)

			if (d->file_system == fs)  return 0L;

	/* include new file system into chain of file systems */

		d->file_system->next = active_fs;

		active_fs = d->file_system;

		return (long)&kernelinfo;  /* return pointer to kernel info as OK */

	} else if (cmd == (int)FS_MOUNT) {  /* install a new gemdos-only device for this FS */

		struct fs_descr *d = (struct fs_descr*)arg;

		FILESYS *fs;

		UNIFILE *u;



	/* first check for existing names */

		r = uni_lookup(dir, name, &fc);

		if (r == 0) {

			release_cookie(&fc);

			return EACCDN;   /* name exists already */

		}

		if (r != EFILNF) return r; /* some other error */

		if (!d) return EACCDN;

		if (!d->file_system) return EACCDN;

	/* check if FS is installed */

		for (fs = active_fs;  fs;  fs = fs->next)

			if (d->file_system == fs)  break;

		if (!fs) return EACCDN;  /* not installed, so return an error */

		u = kmalloc(SIZEOF(UNIFILE));

		if (!u) return EACCDN;

		strncpy(u->name, name, NAME_MAX);

		u->name[NAME_MAX] = 0;

		u->mode = S_IFDIR|DEFAULT_DIRMODE;

		u->data = 0;

		u->fs = d->file_system;

	/* now get the file system its own device number */

		u->dev = d->dev_no = curr_dev_no++;

	/* chain new entry into unifile list */

		u->next = u_root;

		u_root = u;

		return (long)u->dev;

	} else if (cmd == (int)FS_UNMOUNT) {  /* remove a file system's directory */

		struct fs_descr *d = (struct fs_descr*)arg;

		FILESYS *fs;

		UNIFILE *u;



	/* first check that directory exists */

	/* use special uni_lookup mode to get the unifile entry */

		r = do_ulookup(dir, name, &fc, &u);

		if (r != 0)  return EFILNF;   /* name does not exist */

		if (!d) return EFILNF;

		if (!d->file_system) return EFILNF;

		if (d->file_system != fc.fs)

			return EFILNF;  /* not the right name! */

		release_cookie(&fc);



		if (!u || (u->fs != d->file_system))

			return EFILNF;

	/* check if FS is installed */

		for (fs = active_fs;  fs;  fs = fs->next)

			if (d->file_system == fs)  break;

		if (!fs) return EACCDN;  /* not installed, so return an error */



	/* here comes the difficult part: we have to close all files on that

	 * device, so we have to call changedrv(). The file system driver

	 * has to make sure that further calls to fs.root() with this device

	 * number will fail!

	 *

	 * Kludge: mark the directory as a link, so uni_remove will remove it.

	 */

		changedrv(u->dev);

		u->mode &= ~S_IFMT;

		u->mode |= S_IFLNK;

		return uni_remove(dir, name);

	} else if (cmd == (int)FS_UNINSTALL) {    /* remove file system from kernel list */

		struct fs_descr *d = (struct fs_descr*)arg;

		FILESYS *fs, *last_fs;

		UNIFILE *u;



	/* first check if there are any files or directories associated with

	 * this file system

	 */

		for (u = u_root;  u;  u = u->next)

			if (u->fs == d->file_system)

				return EACCDN;   /* we cannot remove it before unmount */

		last_fs = 0;

		fs = active_fs;

		while (fs)  {   /* go through the list and remove the file system */

			if (fs == d->file_system)  {

				if (last_fs)

					last_fs->next = fs->next;

				else

					active_fs = fs->next;

				d->file_system->next = 0;

				return 0;

			}

			last_fs = fs;

			fs = fs->next;

		}

		return EFILNF;

	} else {

	/* see if we should just pass this along to another file system */

		r = uni_lookup(dir, name, &fc);

		if (r == 0) {

			if (fc.fs != &uni_filesys) {

				r = (*fc.fs->fscntl)(&fc, ".", cmd, arg);

				release_cookie(&fc);

				return r;

			}

			release_cookie(&fc);

		}

	}

	return EINVFN;

}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.