/* Copyright 1991,1992 Eric R. Smith. All rights reserved. */ /* a simple unified file system */ #include "mint.h" extern FILESYS bios_filesys, proc_filesys, pipe_filesys, shm_filesys; static long uni_root P_((int drv, fcookie *fc)); static long uni_lookup P_((fcookie *dir, const char *name, fcookie *fc)); static long uni_getxattr P_((fcookie *fc, XATTR *xattr)); static long uni_chattr P_((fcookie *fc, int attrib)); static long uni_chown P_((fcookie *fc, int uid, int gid)); static long uni_chmode P_((fcookie *fc, unsigned mode)); static long uni_rmdir P_((fcookie *dir, const char *name)); static long uni_remove P_((fcookie *dir, const char *name)); static long uni_getname P_((fcookie *root, fcookie *dir, char *pathname)); static long uni_rename P_((fcookie *olddir, char *oldname, fcookie *newdir, const char *newname)); static long uni_opendir P_((DIR *dirh, int flags)); static long uni_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *)); static long uni_rewinddir P_((DIR *dirh)); static long uni_closedir P_((DIR *dirh)); static long uni_pathconf P_((fcookie *dir, int which)); static long uni_dfree P_((fcookie *dir, long *buf)); static DEVDRV * uni_getdev P_((fcookie *fc, long *devsp)); static long uni_symlink P_((fcookie *dir, const char *name, const char *to)); static long uni_readlink P_((fcookie *fc, char *buf, int buflen)); FILESYS uni_filesys = { (FILESYS *)0, 0, 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, nofscntl, 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]; short mode; ushort dev; FILESYS *fs; void *data; struct unifile *next; } UNIFILE; /* the "+1" is for shared memory, which has no BIOS drive */ #define UNI_DIRS NUM_DRIVES+1 static UNIFILE u_drvs[UNI_DIRS]; static UNIFILE *u_root = 0; void unifs_init() { UNIFILE *u = u_drvs; int i; u_root = u; for (i = 0; i < NUM_DRIVES; i++,u++) { u->next = u+1; u->mode = S_IFDIR|DEFAULT_DIRMODE; u->dev = i; u->fs = 0; if (i == PROCDRV) strcpy(u->name, "proc"); else if (i == PIPEDRV) strcpy(u->name, "pipe"); else if (i == BIOSDRV) strcpy(u->name, "dev"); else if (i == UNIDRV) { (u-1)->next = u->next; /* skip this drive */ } else { u->name[0] = i + 'a'; u->name[1] = 0; } } u->next = 0; u->mode = S_IFDIR|DEFAULT_DIRMODE; u->dev = SHMDEVICE; u->fs = &shm_filesys; strcpy(u->name, "shm"); } static long 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 uni_lookup(dir, name, fc) fcookie *dir; const char *name; fcookie *fc; { UNIFILE *u; long drvs; FILESYS *fs; 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, "..")) { *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 (!stricmp(name, u->name)) { if ( (u->mode & S_IFMT) == S_IFDIR ) { if (u->dev >= NUM_DRIVES) { fs = u->fs; return (*fs->root)(u->dev,fc); } if ((drvs & (1L << u->dev)) == 0) return EPTHNF; *fc = curproc->root[u->dev]; if (!fc->fs) { /* drive changed? */ changedrv(fc->dev); *fc = curproc->root[u->dev]; if (!fc->fs) return EPTHNF; } } else { /* a symbolic link */ fc->fs = &uni_filesys; fc->dev = UNIDRV; fc->index = (long)u; } return 0; } } DEBUG("uni_lookup: name (%s) not found", name); return EFILNF; } static long 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 = 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 = 0; xattr->mdate = xattr->adate = xattr->cdate = 0; return 0; } static long uni_chattr(dir, attrib) fcookie *dir; int attrib; { return EACCDN; } static long uni_chown(dir, uid, gid) fcookie *dir; int uid, gid; { return EINVFN; } static long uni_chmode(dir, mode) fcookie *dir; unsigned mode; { return EINVFN; } static long 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 uni_remove(dir, name) fcookie *dir; const char *name; { UNIFILE *u, *lastu; lastu = 0; u = u_root; while (u) { if (!strncmp(u->name, name, NAME_MAX)) { if ( (u->mode & S_IFMT) != S_IFLNK ) return EFILNF; 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 uni_getname(root, dir, pathname) fcookie *root, *dir; char *pathname; { FILESYS *fs; UNIFILE *u; char *n; fcookie relto; 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++ = '\\'; for (n = u->name; *n; ) *pathname++ = *n++; 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) { return (*fs->getname)(&relto, dir, pathname); } 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; } return (*fs->getname)(&curproc->root[dir->dev], dir, pathname); } static long uni_rename(olddir, oldname, newdir, newname) fcookie *olddir; char *oldname; fcookie *newdir; const char *newname; { UNIFILE *u = 0; fcookie fc; long r; for (u = u_root; u; u = u->next) { if (!stricmp(u->name, oldname)) 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 != EFILNF) { DEBUG("uni_rename: error %ld", r); return (r == 0) ? EACCDN : r; } (void)strncpy(u->name, newname, NAME_MAX); return 0; } static long uni_opendir(dirh, flags) DIR *dirh; int flags; { if (dirh->fc.index != 0) { DEBUG("uni_opendir: bad directory"); return EPTHNF; } dirh->index = 0; return 0; } static long 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; } *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 -= sizeof(long); if (namelen <= 0) return ERANGE; *((long *)name) = index; name += sizeof(long); } strncpy(name, dirname, namelen-1); if (strlen(name) < strlen(dirname)) return ENAMETOOLONG; return 0; } static long uni_rewinddir(dirh) DIR *dirh; { dirh->index = 0; return 0; } static long uni_closedir(dirh) DIR *dirh; { return 0; } static long uni_pathconf(dir, which) fcookie *dir; int which; { 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; default: return EINVFN; } } static long uni_dfree(dir, buf) fcookie *dir; long *buf; { 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 * uni_getdev(fc, devsp) fcookie *fc; long *devsp; { *devsp = EACCDN; return 0; } static long 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) return EACCDN; /* file already exists */ if (r != EFILNF) return r; /* some other error */ 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->ruid; u->next = u_root; u->fs = &uni_filesys; u_root = u; return 0; } static long 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; }