/* * xfs.c -- extended file system for master v5.7 * Author : Edgar R”der * Created On : Mon Apr 30 13:18:06 1990 * Last Modified By: Edgar Roeder * Last Modified On: Mon Sep 3 19:25:16 1990 * Update Count : 134 * Status : should work now, needs some speedup * * HISTORY * 25-May-1990 Edgar R”der * Last Modified: Fri May 25 22:31:12 1990 #127 (Edgar R”der) * removed almost all debug output and canonicalize()-calls * put dta_getname in module_header, so other programs (!= Master) * can find it there * * 8-May-1990 Edgar R”der * Last Modified: Tue May 8 21:11:36 1990 #114 (Edgar R”der) * disable dta_getname while dispatcher is active * * 8-May-1990 Edgar R”der * Last Modified: Tue May 8 15:29:08 1990 #107 (Edgar R”der) * reset magic number in dta at the end of Fsnext * add glob to do filename matching * removed some debugging output and made cacheing configurable * * 7-May-1990 Edgar R”der * Last Modified: Sun May 6 23:04:48 1990 #91 (Edgar R”der) * added Fforce and Fdup support * * 6-May-1990 Edgar R”der * Last Modified: Sun May 6 00:21:32 1990 #90 (Edgar R”der) * added _unx2dos call in xfs_opendir to get an absolute path * use XFSMODE environment variable to allow different unixmodes * for xfs and application programs (they could use mode == "cu") * * 4-May-1990 Edgar R”der * Last Modified: Fri May 4 14:55:46 1990 #74 (Edgar R”der) * added rewinddir if directory was already open * added read/write/lseek for device manipulation * * 4-May-1990 Edgar R”der * Last Modified: Fri May 4 02:13:34 1990 #71 (Edgar R”der) * if directory was already open, use that structure * * 4-May-1990 Edgar R”der * Last Modified: Fri May 4 01:57:04 1990 #69 (Edgar R”der) * added special case for deleted files (pattern is 0xE5) * * 4-May-1990 Edgar R”der * changed data structure to record pattern and search attribute * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* to enable debug output define TRACING to 1 */ #define TRACING 1 /* enable debug output */ /* if you use the GNU-malloc */ /*undef GNU_MALLOC /* enable malloc statistics */ /* directory cacheing might interfere with Ddelete and Fdelete */ /*undef DIR_CACHE /* enable directory cacheing */ /* if we don't use glob, we pretend that Fsfirst is either called with */ /* a full filename or with *.* */ #define USE_GLOB /* enable complex search patterns */ #ifdef bool #undef bool #endif typedef short bool; BASEPAGE *master = (BASEPAGE *) 0; char *master_var; #define kByte * 1024L static char __patch_stack[] = "{PatchVar}stack = %ld bytes"; extern long _initial_stack = 100 kByte; static char __patch_var_format[] = "{PatchVar}default for unixmode = %15s"; static char __default_unixmode[16] = "/.,LAHdb\0\0\0\0\0\0\0"; extern char *_default_unixmode = __default_unixmode; extern char _tCASE; extern char _tDOTS; extern char _tSLASH; extern char _tUNLIMITED; extern char *_ltoa(); extern long modul(const char *); extern char *dta_getname(struct _dta *); typedef struct { char *master_name; short function_type; long (*function_entry)(const char *cmdline); } func; func module_entries[] = { { "XFS", 0, modul } }; #define _MODUL_MAGIC 0x58465332L /* 'XFS1' */ #define _MMX_MAGIC 0x4D4D5845L /* 'MMXE' */ #define _XFS_VERSION 2 typedef char *(dta_func)(struct _dta *); struct head { char *module_description; short module_version; short number_of_functions; func *jump_table; long module_magic; long xbra_magic; /* 'XBRA' */ long mmx_magic; /* 'MMXE' */ xptr next; short jump; dta_func *this; } module_header = { " Master-Module-eXtension eXtended File System V1.1 (" __DATE__ " " __TIME__ ")\r\n" "\n" " Usage | Explanation\r\n" " ------------------------+--------------------------\r\n" " xfs help | print this help message\r\n" #ifdef GNU_MALLOC " xfs stat | show memory statistics\r\n" #endif #if TRACING " xfs debug | enable/disable debug\r\n" #endif " xfs start | start new file system\r\n" " | and read $XFSMODE\r\n" " xfs stop | stop new file system\r\n" " xfs ln | install a symbolic link\r\n" "\n" " You should enable the file system with 'virgin 64'!\r\n" "", _XFS_VERSION, 1, module_entries, _MODUL_MAGIC, _XBRA_MAGIC, _MMX_MAGIC, (xptr) 0, _JMP_OPCODE, dta_getname }; static char dospath[FILENAME_MAX]; static char dospath2[FILENAME_MAX]; #define WRITE(f, text) write(f, text, strlen(text)) #ifdef GNU_MALLOC /* Statistics available to the user. */ struct mstats { size_t bytes_total; /* Total size of the heap. */ size_t chunks_used; /* Chunks allocated by the user. */ size_t bytes_used; /* Byte total of user-allocated chunks. */ size_t chunks_free; /* Chunks in the free list. */ size_t bytes_free; /* Byte total of chunks in the free list. */ }; /* Pick up the current statistics. */ extern struct mstats mstats(); void memory_statistics() { struct mstats ms; char output[30]; ms = mstats(); WRITE(1, "Memory-Statistics:\r\n\theapsize\t= "); _ltoa(ms.bytes_total, output, 10); WRITE(1, output); WRITE(1, "\r\n\tchunks used\t= "); _ltoa(ms.chunks_used, output, 10); WRITE(1, output); WRITE(1, "\r\n\tbytes used\t= "); _ltoa(ms.bytes_used, output, 10); WRITE(1, output); WRITE(1, "\r\n\tchunks free\t= "); _ltoa(ms.chunks_free, output, 10); WRITE(1, output); WRITE(1, "\r\n\tbytes free\t= "); _ltoa(ms.bytes_free, output, 10); WRITE(1, output); write(1, "\r\n", 2); } #endif #if TRACING /* use BIOS i/o for debugging output since we might get called from GEM */ static short debugging = 0; static char output[80]; void Bconws(const char *str) { if(!debugging) return; while(*str) { if(*str == '\n') Bconout(CON, '\r'); Bconout(CON, *str++); } } #endif #define DIR_MAGIC 0x4653 /* 'FS' */ #define DIR_STRUCT 0 #define SINGLE_FILE 1 struct Directory { short magic; char type; unsigned char search_attribute; char *path; char *pattern; union { DIR *dir; struct stat *statbuf; } contents; }; static bool dispatcher_active = 1; /* * The reserved part of the dta buffer is used to pass information * from Fsfirst to Fsnext and dta_getname. * The layout is as follows: * * 0 +--------+ * | 'XFS1' | ---> validate information * 4 +--------+ * | DIRECT | ---> pointer to directory structure * 8 +--------+ * | | * 12 +--------+ * | OFFSET | ---> current position in directory * 16 +--------+ * : * 43 +--------+ * | 'x' | ---> validate information * 44 +--------+ * * All other fields are unchanged. */ char * dta_getname(struct _dta *dta) { struct Directory *dir; off_t dirpos; struct dirent *file; if(dispatcher_active & 1) return(dta->dta_name); #if TRACING if(*((long *) &(dta->dta_buf[0])) != _MODUL_MAGIC) { Bconws("dta_getname: magic num XFS1 not found\n"); goto go_out; } if(dta->dta_name[13] != 'x') { Bconws("dta_getname: name[13] != 'x'\n"); goto go_out; } if(!(dir = *((struct Directory **) &(dta->dta_buf[4])))) { Bconws("dta_getname: no Directory struct\n"); goto go_out; } if(dir->magic != DIR_MAGIC) { Bconws("dta_getname: magic num FS not found\n"); go_out: return(dta->dta_name); } #else if(*((long *) &(dta->dta_buf[0])) != _MODUL_MAGIC || (dta->dta_name[13] != 'x') || !(dir = *((struct Directory **) &(dta->dta_buf[4]))) || (dir->magic != DIR_MAGIC)) return(dta->dta_name); #endif if(dir->type == SINGLE_FILE) return(dir->pattern); dirpos = *((short *) &(dta->dta_buf[12])); seekdir(dir->contents.dir, dirpos); file = readdir(dir->contents.dir); if(!file) return(dta->dta_name); return(file->d_name); } extern xptr m_set_dtaname(void (*)()); extern char *m_getenv(const char *); void start_xfs() { char *unixmode = m_getenv("XFSMODE"); if(unixmode) _set_unixmode(unixmode); else if(errno == EINVAL) _set_unixmode(_default_unixmode); errno = ENOERR; if(!dispatcher_active) return; module_header.next = m_set_dtaname(_XBRA_VEC(module_header)); dispatcher_active = 0; } void stop_xfs() { if(dispatcher_active) return; dispatcher_active = 1; m_set_dtaname((void (*)()) module_header.next); } long modul(const char *cmdlin) { int retval; if(!*cmdlin) { return(0); } else if(!strcmp(cmdlin, "help")) { WRITE(1, module_header.module_description); #ifdef GNU_MALLOC } else if(!strcmp(cmdlin, "stat")) { memory_statistics(); #endif #if TRACING } else if(!strcmp(cmdlin, "debug")) { debugging = !debugging; #endif } else if(!strcmp(cmdlin, "start")) { start_xfs(); } else if(!strcmp(cmdlin, "stop")) { stop_xfs(); } else if(!strcmp(cmdlin, "ln")) { char *old, *new; strcpy(dospath, cmdlin); new = strtok(dospath, " \t"); old = strtok(NULL, " \t"); if(!old || !new) return(1); if(symlink(old, new)) { retval = -errno; errno = ENOERR; return(retval); } } return(0); } struct head *current; static long my_cookies[16] = { _MODUL_MAGIC, (long) &module_header, 0L, 8L }; void remove_cookies() { set_sysvar_to_long(_p_cookies, 0L); } void remove_cookie() { long *cp; if(cp = (long *) get_sysvar(_p_cookies)) { while(*cp && *cp != _MODUL_MAGIC) cp += 2; while(*cp) { cp[0] = cp[2]; cp[1] = cp[3]; cp += 2; } } } #define SUPERBIT (1L << 13) #define DCREATE 57 #define DDELETE 58 #define DSETPATH 59 #define FCREATE 60 #define FOPEN 61 #define FCLOSE 62 #define FREAD 63 #define FWRITE 64 #define FDELETE 65 #define FSEEK 66 #define FATTRIB 67 #define FDUP 69 #define FFORCE 70 #define DGETPATH 71 #define PEXEC 75 #define FSFIRST 78 #define FSNEXT 79 #define FRENAME 86 #define FDATIME 87 static long trap1_dispatcher(char *); static long new_trap1(long); static xbra_struct Trap1 = _TRAP_INIT(new_trap1); static jmp_buf oldTrap; static long new_trap1(long arg) { char *sp; register long ret; if(dispatcher_active & 1) goto old_trap1; /* ATTENTION: setjmp uses trap #1, so we have to disable this now */ dispatcher_active = 1; if(setjmp(oldTrap)) { dispatcher_active = 0; old_trap1: asm("movel %0, sp" :: "g" (&arg)); asm("jmp %0@" :: "a" (Trap1.next)); } if(*((short *) &arg) & SUPERBIT) { sp = ((char *) &arg) + 6; } else { asm("movel usp, %0" : "=a" (sp)); } /* now sp points to our argument list */ ret = trap1_dispatcher(sp); dispatcher_active = 0; asm("movel %0, sp" :: "g" (&arg)); /* for the next asm to work,*/ /* ret should better not be */ /* addressed relative to sp */ asm("movel %0, d0" :: "d" (ret)); asm("rte"); } extern int _unx2dos(const char * const, char * const); extern int _dos2unx(const char * const, char * const); #ifdef strcpy #undef strcpy #endif extern DIR *__opendir_chain; static void xfs_closedir(struct Directory *dir) { if(!dir || dir->magic != DIR_MAGIC) return; free(dir->path); free(dir->pattern); if(dir->type == SINGLE_FILE) free(dir->contents.statbuf); else closedir(dir->contents.dir); } static inline struct Directory * xfs_opendir(const char *path, struct Directory *old_dir) { char *pattern; struct Directory *dd; struct stat *st; if((!(dd = old_dir) || (old_dir->magic != DIR_MAGIC)) && !(dd = malloc((size_t)sizeof(struct Directory)))) { errno = ENOMEM; return NULL; } strcpy(dospath, path); do { if(!(pattern = strrchr(dospath, '\\')) && !(pattern = strrchr(dospath, '/'))) { pattern = dospath; } else { *pattern++ = '\0'; } } while(pattern != dospath && !*pattern); #ifdef CACHE_DIR /* this cacheing of open directories is problematic, since */ /* deleted files will still be listed */ if((dd == old_dir) && !strcmp(dospath, dd->path) && !strcmp(pattern, dd->pattern)) { if(dd->type == DIR_STRUCT) rewinddir(dd->contents.dir); return(dd); } else #endif xfs_closedir(old_dir); dd->magic = DIR_MAGIC; dd->path = strdup(dospath); dd->pattern = strdup(pattern); #ifdef USE_GLOB /* look for glob-metacharacters in pattern */ if(glob(pattern, "*[*?[\\]{}]*")) #else if(!strcmp(pattern, "*.*")) #endif { if(pattern == dospath) strcpy(dospath, "."); if(!*dospath) strcpy(dospath, "\\"); dd->type = DIR_STRUCT; if(!(dd->contents.dir = opendir(dospath))) { xfs_closedir(dd); free(dd); return(NULL); } } else { #ifndef USE_GLOB /* FIXME: for now assume that pattern is either *.* or */ /* a normal filename (exclude things like a*.b), could */ /* be done with glob() */ #endif dd->type = SINGLE_FILE; if(!(st = malloc((size_t)sizeof(struct stat)))) { errno = ENOMEM; free(dd->path); free(dd->pattern); free(dd); return NULL; } dd->contents.statbuf = st; if(pattern != dospath) pattern[-1] = '\\'; if(stat(dospath, st) < 0) { errno = ENOENT; xfs_closedir(dd); return NULL; } } return(dd); } static dev_t forced_standard_handle[3]; static long trap1_dispatcher(char *sp) { long ret; int save_errno; char *path; char save_tSLASH; struct _device *device; struct _dta *dtabuf; save_errno = errno; errno = ENOERR; path = *((char **) &sp[2]); switch(*((short *) sp)) { case DCREATE : ret = mkdir(path); break; case DDELETE : ret = rmdir(path); break; case DSETPATH : ret = chdir(path); break; case FCREATE : ret = creat(path, 0644); break; /* does not support file modes */ case FOPEN : ret = open(path, *((short *) &sp[6])); break; case FCLOSE : { short fd; fd = *((short *) &sp[2]); if(fd >= 0 && fd <= 2 && forced_standard_handle[fd]) fd = forced_standard_handle[fd]; ret = close(fd); break; } case FREAD : { short fd; fd = *((short *) &sp[2]); if(fd >= 0 && fd <= 2 && forced_standard_handle[fd]) fd = forced_standard_handle[fd]; ret = read(fd, *((void **) &sp[8]), *((long *) &sp[4])); break; } case FWRITE : { short fd; fd = *((short *) &sp[2]); if(fd >= 0 && fd <= 2 && forced_standard_handle[fd]) fd = forced_standard_handle[fd]; ret = write(fd, *((void **) &sp[8]), *((long *) &sp[4])); break; } case FDELETE : /* may get trouble if file is open */ ret = unlink(path); break; case FSEEK : { short fd; fd = *((short *) &sp[2]); if(fd >= 0 && fd <= 2 && forced_standard_handle[fd]) fd = forced_standard_handle[fd]; ret = lseek(*((short *) &sp[6]), fd, *((short *) &sp[8])); break; } case FATTRIB : _unx2dos(path, dospath); *((char **) &sp[2]) = dospath; goto do_old_trap; case FDUP : { short fd; fd = *((short *) &sp[2]); if(fd >= 0 && fd <= 2 && (ret = forced_standard_handle[fd])) break; goto do_old_trap; } case FFORCE : { short std_fd = *((short *) &sp[2]); short nstd_fd = *((short *) &sp[4]); if(std_fd == 0) { if((device = _dev_devnum(nstd_fd)) && device->d_read) { forced_standard_handle[0] = nstd_fd; ret = 0; break; } else { forced_standard_handle[0] = 0; goto do_old_trap; } } else if(std_fd == 1 || std_fd == 2) { if((device = _dev_devnum(nstd_fd)) && device->d_write) { forced_standard_handle[std_fd] = nstd_fd; ret = 0; break; } else { forced_standard_handle[std_fd] = 0; goto do_old_trap; } } goto do_old_trap; } case DGETPATH : { short drive; short save_drive; drive = *((short *) &sp[6]); if (!drive) dospath[0] = Dgetdrv() + 'A'; else dospath[0] = drive - 1 + 'A'; dospath[1] = ':'; ret = Dgetpath(dospath+2, drive); save_tSLASH = _tSLASH; _tSLASH = 0; _dos2unx(dospath, dospath2); _tSLASH = save_tSLASH; if (dospath2[0] && dospath2[1] == ':') strcpy(path, dospath2+2); else strcpy(path, dospath2); break; } case PEXEC : { char *path; path = *((char **) &sp[4]); _unx2dos(path, dospath); *((char **) &sp[4]) = dospath; goto do_old_trap; } case FSFIRST : { struct Directory *dir; if((*path == '\345') || (*((short *) &sp[6]) & 0x08)) goto do_old_trap; dtabuf = (struct _dta *) Fgetdta(); if(*((long *) &(dtabuf->dta_buf[0])) == _MODUL_MAGIC) dir = *((struct Directory **) &(dtabuf->dta_buf[4])); else dir = NULL; if(!(dir = xfs_opendir(path, dir))) break; dir->search_attribute = (*((short *) &sp[6]) | 0x21) & 0xFF; dtabuf = (struct _dta *) Fgetdta(); *((long *) &(dtabuf->dta_buf[0])) = _MODUL_MAGIC; *((struct Directory **) &(dtabuf->dta_buf[4])) = dir; if(dir->type == DIR_STRUCT) *((long *) &(dtabuf->dta_buf[12])) = telldir(dir->contents.dir); } case FSNEXT : { struct Directory *dir; long dirpos; dtabuf = (struct _dta *) Fgetdta(); if(*((long *) &(dtabuf->dta_buf[0])) != _MODUL_MAGIC) { #if TRACING Bconws("Fsnext: magic num XFS1 not found\n"); #endif errno = EINVAL; break; } dir = *((struct Directory **) &(dtabuf->dta_buf[4])); if(!dir || (dir->magic != DIR_MAGIC)) { #if TRACING Bconws("Fsnext: directory struct invalid\n"); #endif errno = EINVAL; break; } if(dir->type == SINGLE_FILE) { union { DOSTIME dt; time_t tt; } ft; path = dir->pattern; ft.tt = dostime(dir->contents.statbuf->st_mtime); dtabuf->dta_attribute = dir->contents.statbuf->st_attr; dtabuf->dta_time = ft.dt.time; dtabuf->dta_date = ft.dt.date; dtabuf->dta_size = dir->contents.statbuf->st_size; } else { struct dirent *file; if(!dir->contents.dir) { #if TRACING Bconws("Fsnext: DIR not open\n"); #endif errno = EINVAL; break; } dirpos = *((short *) &(dtabuf->dta_buf[14])); if(dirpos != telldir(dir->contents.dir)) seekdir(dir->contents.dir, dirpos); #ifdef USE_GLOB do { #endif file = readdir(dir->contents.dir); #ifdef USE_GLOB /* we try as long as we have a valid */ /* file, but no matching file */ } while( file && !( /* a matching file would be any file */ /* with attribute 0 or an attribute */ (!file->d_attribute /* matching the search attribute and */ || (file->d_attribute & dir->search_attribute)) /* a name matching the searchpattern */ && (!strcmp(dir->pattern, "*.*") || glob(file->d_name, dir->pattern) ) ) ); #endif if(!file) { errno = ENMFILES; break; } path = file->d_name; dtabuf->dta_attribute = file->d_attribute; dtabuf->dta_time = file->d_time; dtabuf->dta_date = file->d_date; dtabuf->dta_size = file->d_size; } if(!strcmp(path, ".") || !strcmp(path, "..")) { strcpy(dtabuf->dta_name, path); } else { _unx2dos(path, dospath); strcpy(dtabuf->dta_name, strrchr(dospath, '\\') + 1); } *((long *) &(dtabuf->dta_buf[0])) = _MODUL_MAGIC; *((struct Directory **) &(dtabuf->dta_buf[4])) = dir; if(dir->type == DIR_STRUCT) { dirpos = (dirpos << 16) | telldir(dir->contents.dir) & 0x0000FFFFL; /* offset 12 used to make st_inode() happy */ *((long *) &(dtabuf->dta_buf[12])) = dirpos; } dtabuf->dta_name[13] = 'x'; ret = 0; errno = ENOERR; break; } case FRENAME : ret = rename(path, *((char **) &sp[6])); break; default : do_old_trap: errno = save_errno; longjmp(oldTrap, 1); } if(errno) ret = - errno; errno = save_errno; return(ret); } #define TRAP1 33 void init_module(void) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); Trap1.gnuc_magic = _MODUL_MAGIC; Trap1.next = (xptr) Setexc(TRAP1, _TRAP_VEC(TRAP1, Trap1)); } void finish_module(void) { stop_xfs(); unlink_handler(&Trap1, TRAP1); } void install_cookie(int resident) { long *cp; long cookies_used = 0; long tmp; if(!(cp = (long *) get_sysvar(_p_cookies))) { if(!resident) { write(2, "Unable to install cookie!\r\n", 27); exit(1); } signal(SIGRESET, remove_cookies); set_sysvar_to_long(_p_cookies, (long)(cp = my_cookies)); } while(*cp && *cp != _MODUL_MAGIC) { cp += 2; cookies_used++; } if(*cp) { current = (struct head *) cp[1]; if(current == &module_header) init_module(); return; } if(!(cp[1] - cookies_used - 1)) { write(2, "No room for cookie!\r\n", 21); exit(1); } *cp++ = _MODUL_MAGIC; tmp = *cp; *cp++ = (long) (current = &module_header); *cp++ = 0L; *cp = tmp; init_module(); return; } void install_module(int resident, char *firstcmd) { long keep; install_cookie(resident); if(master) { strcpy(master_var, "Master-Module "); _ltoa(current, master_var+14, 10); } if(current != &module_header) { (*(current->jump_table[0].function_entry))(firstcmd); exit(0); } if(!*firstcmd) modul("help"); else modul(firstcmd); if(!resident) return; keep = _base->p_tlen + _base->p_dlen + _base->p_blen + sizeof(BASEPAGE) + _initial_stack; _base -= 2; Ptermres(keep, 0); } void remove_module() { finish_module(); remove_cookie(); } void usage(char *prog) { write(2, "Usage: [ module ] ", 18); WRITE(2, prog); write(2, " [ ]\r\n", 12); exit(2); } main(int argc, char *argv[]) { char *mmx; BASEPAGE *caller; char *prog; char *firstcmd; if(**argv) prog = *argv; else prog = "xfs"; argc--; argv++; if(argc) { firstcmd = *argv; argc--; argv++; } else firstcmd = ""; if(argc) usage(prog); if(mmx = getenv("MMX")) { if(strncmp(mmx, "Master-Call", 11)) exit(1); mmx += 11; if(!(master = (BASEPAGE *) strtol(mmx, &mmx, 10)) || !(master_var = (char *) strtol(mmx, &mmx, 10)) || !(caller = (BASEPAGE *) strtol(mmx, &mmx, 10)) || (caller != _base->p_parent)) exit(1); install_module(1, firstcmd); } else if(!system(NULL)) install_module(1, firstcmd); install_module(0, firstcmd); system("-i"); remove_module(); }