--- uae/src/filesys.c 2018/04/24 16:38:39 1.1 +++ uae/src/filesys.c 2018/04/24 16:47:32 1.1.1.7 @@ -3,9 +3,10 @@ * * Unix file system handler for AmigaDOS * - * Copyright 1996 Ed Hanway, Bernd Schmidt + * Copyright 1996 Ed Hanway + * Copyright 1996, 1997 Bernd Schmidt * - * Version 0.2: 960730 + * Version 0.4: 970308 * * Based on example code (c) 1988 The Software Distillery * and published in Transactor for the Amiga, Volume 2, Issues 2-5. @@ -13,120 +14,408 @@ * * Known limitations: * Does not support ACTION_INHIBIT (big deal). - * Does not support any 2.0+ packet types (except ACTION_SAME_LOCK) - * Does not actually enforce exclusive locks. + * Does not support several 2.0+ packet types. * Does not support removable volumes. * May not return the correct error code in some cases. * Does not check for sane values passed by AmigaDOS. May crash the emulation * if passed garbage values. - * - * TODO someday: - * Implement real locking using flock. Needs test cases. + * Could do tighter checks on malloc return values. + * Will probably fail spectacularly in some cases if the filesystem is + * modified at the same time by another process while UAE is running. */ #include "sysconfig.h" #include "sysdeps.h" #include "config.h" +#include "threaddep/penguin.h" #include "options.h" +#include "uae.h" #include "memory.h" #include "custom.h" -#include "events.h" +#include "readcpu.h" #include "newcpu.h" -#include "xwin.h" +#include "filesys.h" #include "autoconf.h" #include "compiler.h" +#include "fsusage.h" +#include "native2amiga.h" +#include "scsidev.h" +#include "fsdb.h" -#ifdef __bebox__ -#undef access -// This is just a temporary hack. -// (Will be obsolete in future versions of the BeOS) [ So I hope -- Bernd ] +/* #define TRACING_ENABLED */ +#ifdef TRACING_ENABLED +#define TRACE(x) do { write_log x; } while(0) +#define DUMPLOCK(u,x) dumplock(u,x) +#else +#define TRACE(x) +#define DUMPLOCK(u,x) +#endif -int access(const char *name, int mode) +static long dos_errno(void) { - struct stat statbuf; -// printf("%s\n",name); + int e = errno; - if(-1 == stat(name, &statbuf)) return(-1); - else{ -// printf("OK\n"); -// printf("%i\n",S_ISDIR(statbuf.st_mode)?1:0); - if(mode&R_OK) if(statbuf.st_mode&S_IRUSR) return(0); - if(mode&W_OK) if(statbuf.st_mode&S_IWUSR) return(0); - return(-1); - } + switch (e) { + case ENOMEM: return ERROR_NO_FREE_STORE; + case EEXIST: return ERROR_OBJECT_EXISTS; + case EACCES: return ERROR_WRITE_PROTECTED; + case ENOENT: return ERROR_OBJECT_NOT_FOUND; + case ENOTDIR: return ERROR_OBJECT_WRONG_TYPE; + case ENOSPC: return ERROR_DISK_IS_FULL; + case EBUSY: return ERROR_OBJECT_IN_USE; + case EISDIR: return ERROR_OBJECT_WRONG_TYPE; +#if defined(ETXTBSY) + case ETXTBSY: return ERROR_OBJECT_IN_USE; +#endif +#if defined(EROFS) + case EROFS: return ERROR_DISK_WRITE_PROTECTED; +#endif +#if defined(ENOTEMPTY) +#if ENOTEMPTY != EEXIST + case ENOTEMPTY: return ERROR_DIRECTORY_NOT_EMPTY; +#endif +#endif + + default: + TRACE(("Unimplemented error %s\n", strerror(e))); + return ERROR_NOT_IMPLEMENTED; + } } + +/* + * This _should_ be no issue for us, but let's rather use a guaranteed + * thread safe function if we have one. + * This used to be broken in glibc versions <= 2.0.1 (I think). I hope + * no one is using this these days. + * Michael Krause says it's also broken in libc5. ARRRGHHHHHH!!!! + */ +#if 0 && defined HAVE_READDIR_R + +static struct dirent *my_readdir (DIR *dirstream, struct dirent *space) +{ + struct dirent *loc; + if (readdir_r (dirstream, space, &loc) == 0) { + /* Success */ + return loc; + } + return 0; +} + +#else +#define my_readdir(dirstream, space) readdir(dirstream) #endif -#define MAKE_CASE_INSENSITIVE +uaecptr filesys_initcode; +static uae_u32 fsdevname, filesys_configdev; + +#define FS_STARTUP 0 +#define FS_GO_DOWN 1 typedef struct { char *devname; /* device name, e.g. UAE0: */ - CPTR devname_amiga; - CPTR startup; + uaecptr devname_amiga; + uaecptr startup; char *volname; /* volume name, e.g. CDROM, WORK, etc. */ char *rootdir; /* root unix directory */ int readonly; /* disallow write access? */ int devno; + + struct hardfiledata hf; + + /* Threading stuff */ + smp_comm_pipe *unit_pipe, *back_pipe; + penguin_id tid; + struct _unit *volatile self; + /* Reset handling */ + uae_sem_t reset_sync_sem; + int reset_state; } UnitInfo; #define MAX_UNITS 20 -static int num_units = 0, num_filesys_units = 0; -static UnitInfo ui[MAX_UNITS]; -static ULONG fsdevname, filesysseglist, hardfileseglist, filesys_configdev; -static CPTR filesys_parampacket; +struct uaedev_mount_info { + int num_units; + UnitInfo ui[MAX_UNITS]; +}; -void add_filesys_unit(char *volname, char *rootdir, int readonly) +static struct uaedev_mount_info *current_mountinfo; + +int nr_units (struct uaedev_mount_info *mountinfo) { - if (num_units >= MAX_UNITS) { - fprintf(stderr, "Maximum number of file systems mounted.\n"); - return; + return mountinfo->num_units; +} + +int is_hardfile (struct uaedev_mount_info *mountinfo, int unit_no) +{ + return mountinfo->ui[unit_no].volname == 0; +} + +static void close_filesys_unit (UnitInfo *uip) +{ + if (uip->hf.fd != 0) + fclose (uip->hf.fd); + if (uip->volname != 0) + free (uip->volname); + if (uip->devname != 0) + free (uip->devname); + if (uip->rootdir != 0) + free (uip->rootdir); + if (uip->unit_pipe) + free (uip->unit_pipe); + if (uip->back_pipe) + free (uip->back_pipe); + + uip->unit_pipe = 0; + uip->back_pipe = 0; + + uip->hf.fd = 0; + uip->volname = 0; + uip->devname = 0; + uip->rootdir = 0; +} + +char *get_filesys_unit (struct uaedev_mount_info *mountinfo, int nr, + char **volname, char **rootdir, int *readonly, + int *secspertrack, int *surfaces, int *reserved, + int *cylinders, int *size, int *blocksize) +{ + UnitInfo *uip = mountinfo->ui + nr; + + if (nr >= mountinfo->num_units) + return "No slot allocated for this unit"; + + *volname = uip->volname ? my_strdup (uip->volname) : 0; + *rootdir = uip->rootdir ? my_strdup (uip->rootdir) : 0; + *readonly = uip->readonly; + *secspertrack = uip->hf.secspertrack; + *surfaces = uip->hf.surfaces; + *reserved = uip->hf.reservedblocks; + *size = uip->hf.size; + *cylinders = uip->hf.nrcyls; + *blocksize = uip->hf.blocksize; + return 0; +} + +static char *set_filesys_unit_1 (struct uaedev_mount_info *mountinfo, int nr, + char *volname, char *rootdir, int readonly, + int secspertrack, int surfaces, int reserved, + int blocksize) +{ + UnitInfo *ui = mountinfo->ui + nr; + + if (nr >= mountinfo->num_units) + return "No slot allocated for this unit"; + + ui->hf.fd = 0; + ui->devname = 0; + ui->volname = 0; + ui->rootdir = 0; + ui->unit_pipe = 0; + ui->back_pipe = 0; + + if (volname != 0) { + ui->volname = my_strdup (volname); + ui->hf.fd = 0; + } else { + ui->volname = 0; + ui->hf.fd = fopen (rootdir, "r+b"); + if (ui->hf.fd == 0) { + readonly = 1; + ui->hf.fd = fopen (rootdir, "rb"); + } + if (ui->hf.fd == 0) + return "Hardfile not found"; + + if (secspertrack < 1 || secspertrack > 32767 + || surfaces < 1 || surfaces > 1023 + || reserved < 0 || reserved > 1023 + || (blocksize & (blocksize - 1)) != 0) + { + return "Bad hardfile geometry"; + } + fseek (ui->hf.fd, 0, SEEK_END); + ui->hf.size = ftell (ui->hf.fd); + ui->hf.secspertrack = secspertrack; + ui->hf.surfaces = surfaces; + ui->hf.reservedblocks = reserved; + ui->hf.nrcyls = (ui->hf.size / blocksize) / (secspertrack * surfaces); + ui->hf.blocksize = blocksize; + } + ui->self = 0; + ui->reset_state = FS_STARTUP; + ui->rootdir = my_strdup (rootdir); + ui->readonly = readonly; + + return 0; +} + +char *set_filesys_unit (struct uaedev_mount_info *mountinfo, int nr, + char *volname, char *rootdir, int readonly, + int secspertrack, int surfaces, int reserved, + int blocksize) +{ + UnitInfo ui = mountinfo->ui[nr]; + char *result = set_filesys_unit_1 (mountinfo, nr, volname, rootdir, readonly, + secspertrack, surfaces, reserved, blocksize); + if (result) + mountinfo->ui[nr] = ui; + else + close_filesys_unit (&ui); + + return result; +} + +char *add_filesys_unit (struct uaedev_mount_info *mountinfo, + char *volname, char *rootdir, int readonly, + int secspertrack, int surfaces, int reserved, + int blocksize) +{ + char *retval; + int nr = mountinfo->num_units; + UnitInfo *uip = mountinfo->ui + nr; + + if (nr >= MAX_UNITS) + return "Maximum number of file systems mounted"; + + mountinfo->num_units++; + retval = set_filesys_unit_1 (mountinfo, nr, volname, rootdir, readonly, + secspertrack, surfaces, reserved, blocksize); + if (retval) + mountinfo->num_units--; + return retval; +} + +int kill_filesys_unit (struct uaedev_mount_info *mountinfo, int nr) +{ + UnitInfo *uip = mountinfo->ui; + if (nr >= mountinfo->num_units || nr < 0) + return -1; + + close_filesys_unit (mountinfo->ui + nr); + + mountinfo->num_units--; + for (; nr < mountinfo->num_units; nr++) { + uip[nr] = uip[nr+1]; } + return 0; +} - if (volname != NULL) { - num_filesys_units++; - ui[num_units].volname = my_strdup(volname); - } else - ui[num_units].volname = NULL; - ui[num_units].rootdir = my_strdup(rootdir); - ui[num_units].readonly = readonly; +int move_filesys_unit (struct uaedev_mount_info *mountinfo, int nr, int to) +{ + UnitInfo tmpui; + UnitInfo *uip = mountinfo->ui; + + if (nr >= mountinfo->num_units || nr < 0 + || to >= mountinfo->num_units || to < 0 + || to == nr) + return -1; + tmpui = uip[nr]; + if (to > nr) { + int i; + for (i = nr; i < to; i++) + uip[i] = uip[i + 1]; + } else { + int i; + for (i = nr; i > to; i--) + uip[i] = uip[i - 1]; + } + uip[to] = tmpui; + return 0; +} - num_units++; +int sprintf_filesys_unit (struct uaedev_mount_info *mountinfo, char *buffer, int num) +{ + UnitInfo *uip = mountinfo->ui; + if (num >= mountinfo->num_units) + return -1; + + if (uip[num].volname != 0) + sprintf (buffer, "(DH%d:) Filesystem, %s: %s %s", num, uip[num].volname, + uip[num].rootdir, uip[num].readonly ? "ro" : ""); + else + sprintf (buffer, "(DH%d:) Hardfile, \"%s\", size %d bytes", num, + uip[num].rootdir, uip[num].hf.size); + return 0; } -void write_filesys_config(FILE *f) +void write_filesys_config (struct uaedev_mount_info *mountinfo, + const char *unexpanded, const char *default_path, FILE *f) { + UnitInfo *uip = mountinfo->ui; int i; - for (i = 0; i < num_units; i++) { - if (ui[i].volname != NULL) { - fprintf(f, "-%c %s:%s\n", ui[i].readonly ? 'M' : 'm', - ui[i].volname, ui[i].rootdir); + + for (i = 0; i < mountinfo->num_units; i++) { + char *str; + str = cfgfile_subst_path (default_path, unexpanded, uip[i].rootdir); + if (uip[i].volname != 0) { + fprintf (f, "filesystem=%s,%s:%s\n", uip[i].readonly ? "ro" : "rw", + uip[i].volname, str); + } else { + fprintf (f, "hardfile=%s,%d,%d,%d,%d,%s\n", uip[i].hf.secspertrack, + uip[i].hf.surfaces, uip[i].hf.reservedblocks, 512, str); } + free (str); } } -#ifdef TRACING_ENABLED -#define TRACE(x) printf x; -#define DUMPLOCK(x) dumplock(x) -#else -#define TRACE(x) -#define DUMPLOCK(x) -#endif +struct uaedev_mount_info *alloc_mountinfo (void) +{ + struct uaedev_mount_info *info; + info = (struct uaedev_mount_info *)malloc (sizeof *info); + /* memset (info, 0xaa, sizeof *info);*/ + info->num_units = 0; + return info; +} + +struct uaedev_mount_info *dup_mountinfo (struct uaedev_mount_info *mip) +{ + int i; + struct uaedev_mount_info *i2 = alloc_mountinfo (); + + memcpy (i2, mip, sizeof *i2); + + for (i = 0; i < i2->num_units; i++) { + UnitInfo *uip = i2->ui + i; + if (uip->volname) + uip->volname = my_strdup (uip->volname); + if (uip->rootdir) + uip->rootdir = my_strdup (uip->rootdir); + if (uip->hf.fd) + uip->hf.fd = fdopen ( dup (fileno (uip->hf.fd)), uip->readonly ? "rb" : "r+b"); + } + return i2; +} + +void free_mountinfo (struct uaedev_mount_info *mip) +{ + int i; + for (i = 0; i < mip->num_units; i++) + close_filesys_unit (mip->ui + i); + free (mip); +} + +struct hardfiledata *get_hardfile_data (int nr) +{ + UnitInfo *uip = current_mountinfo->ui; + if (nr < 0 || nr >= current_mountinfo->num_units || uip[nr].volname != 0) + return 0; + return &uip[nr].hf; +} /* minimal AmigaDOS definitions */ /* field offsets in DosPacket */ -#define dp_Type (8) -#define dp_Res1 (12) -#define dp_Res2 (16) -#define dp_Arg1 (20) -#define dp_Arg2 (24) -#define dp_Arg3 (28) -#define dp_Arg4 (32) +#define dp_Type 8 +#define dp_Res1 12 +#define dp_Res2 16 +#define dp_Arg1 20 +#define dp_Arg2 24 +#define dp_Arg3 28 +#define dp_Arg4 32 /* result codes */ -#define DOS_TRUE (-1L) +#define DOS_TRUE ((unsigned long)-1L) #define DOS_FALSE (0L) /* packet types */ @@ -147,7 +436,6 @@ void write_filesys_config(FILE *f) #define ACTION_SET_COMMENT 28 #define ACTION_PARENT 29 #define ACTION_SET_DATE 34 -#define ACTION_SAME_LOCK 40 #define ACTION_FIND_WRITE 1004 #define ACTION_FIND_INPUT 1005 #define ACTION_FIND_OUTPUT 1006 @@ -157,55 +445,47 @@ void write_filesys_config(FILE *f) #define ACTION_READ 'R' #define ACTION_WRITE 'W' -#define DISK_TYPE (0x444f5301) /* DOS\1 */ +/* 2.0+ packet types */ +#define ACTION_INHIBIT 31 +#define ACTION_SET_FILE_SIZE 1022 +#define ACTION_LOCK_RECORD 2008 +#define ACTION_FREE_RECORD 2009 +#define ACTION_SAME_LOCK 40 +#define ACTION_CHANGE_MODE 1028 +#define ACTION_FH_FROM_LOCK 1026 +#define ACTION_COPY_DIR_FH 1030 +#define ACTION_PARENT_FH 1031 +#define ACTION_EXAMINE_FH 1034 +#define ACTION_EXAMINE_ALL 1033 +#define ACTION_MAKE_LINK 1021 +#define ACTION_READ_LINK 1024 +#define ACTION_FORMAT 1020 +#define ACTION_IS_FILESYSTEM 1027 +#define ACTION_ADD_NOTIFY 4097 +#define ACTION_REMOVE_NOTIFY 4098 -/* errors */ -#define ERROR_NO_FREE_STORE 103 -#define ERROR_OBJECT_IN_USE 202 -#define ERROR_OBJECT_EXISTS 203 -#define ERROR_DIR_NOT_FOUND 204 -#define ERROR_OBJECT_NOT_FOUND 205 -#define ERROR_ACTION_NOT_KNOWN 209 -#define ERROR_OBJECT_WRONG_TYPE 212 -#define ERROR_DISK_WRITE_PROTECTED 214 -#define ERROR_DIRECTORY_NOT_EMPTY 216 -#define ERROR_DEVICE_NOT_MOUNTED 218 -#define ERROR_SEEK_ERROR 219 -#define ERROR_DISK_FULL 221 -#define ERROR_WRITE_PROTECTED 223 -#define ERROR_NO_MORE_ENTRIES 232 -#define ERROR_NOT_IMPLEMENTED 236 +#define DISK_TYPE 0x444f5301 /* DOS\1 */ -static long dos_errno(void) -{ - int e = errno; +typedef struct { + uae_u32 uniq; + a_inode *aino; + DIR* dir; +} ExamineKey; - switch(e) { - case ENOMEM: return ERROR_NO_FREE_STORE; - case EEXIST: return ERROR_OBJECT_EXISTS; - case EACCES: return ERROR_WRITE_PROTECTED; - case ENOENT: return ERROR_OBJECT_NOT_FOUND; - case ENOTDIR: return ERROR_OBJECT_WRONG_TYPE; - case ENOSPC: return ERROR_DISK_FULL; - case EBUSY: return ERROR_OBJECT_IN_USE; - case EISDIR: return ERROR_OBJECT_WRONG_TYPE; -#if defined(ETXTBSY) - case ETXTBSY: return ERROR_OBJECT_IN_USE; -#endif -#if defined(EROFS) - case EROFS: return ERROR_DISK_WRITE_PROTECTED; -#endif -#if defined(ENOTEMPTY) -#if ENOTEMPTY != EEXIST - case ENOTEMPTY: return ERROR_DIRECTORY_NOT_EMPTY; -#endif -#endif +typedef struct key { + struct key *next; + a_inode *aino; + uae_u32 uniq; + int fd; + off_t file_pos; +} Key; - default: - TRACE(("Unimplemented error %s\n", strerror(e))); - return ERROR_NOT_IMPLEMENTED; - } -} +/* Since ACTION_EXAMINE_NEXT is so braindamaged, we have to keep + * some of these around + */ + +#define EXKEYS 100 +#define MAX_AINO_HASH 128 /* handler state info */ @@ -213,510 +493,979 @@ typedef struct _unit { struct _unit *next; /* Amiga stuff */ - CPTR dosbase; - CPTR volume; - CPTR port; /* Our port */ + uaecptr dosbase; + uaecptr volume; + uaecptr port; /* Our port */ + uaecptr locklist; /* Native stuff */ - long unit; /* unit number */ - UnitInfo ui; /* unit startup info */ + uae_s32 unit; /* unit number */ + UnitInfo ui; /* unit startup info */ + char tmpbuf3[256]; + + /* Dummy message processing */ + uaecptr dummy_message; + volatile unsigned int cmds_sent; + volatile unsigned int cmds_complete; + volatile unsigned int cmds_acked; + + /* ExKeys */ + ExamineKey examine_keys[EXKEYS]; + int next_exkey; + + /* Keys */ + struct key *keys; + uae_u32 key_uniq; + uae_u32 a_uniq; + + a_inode rootnode; + unsigned long aino_cache_size; + a_inode *aino_hash[MAX_AINO_HASH]; + unsigned long nr_cache_hits; + unsigned long nr_cache_lookups; } Unit; -typedef struct { - CPTR addr; /* addr of real packet */ - long type; - long res1; - long res2; - long arg1; - long arg2; - long arg3; - long arg4; -} DosPacket; +typedef uae_u8 *dpacket; +#define PUT_PCK_RES1(p,v) do { do_put_mem_long ((uae_u32 *)((p) + dp_Res1), (v)); } while (0) +#define PUT_PCK_RES2(p,v) do { do_put_mem_long ((uae_u32 *)((p) + dp_Res2), (v)); } while (0) +#define GET_PCK_TYPE(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Type)))) +#define GET_PCK_RES1(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Res1)))) +#define GET_PCK_RES2(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Res2)))) +#define GET_PCK_ARG1(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Arg1)))) +#define GET_PCK_ARG2(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Arg2)))) +#define GET_PCK_ARG3(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Arg3)))) +#define GET_PCK_ARG4(p) ((uae_s32)(do_get_mem_long ((uae_u32 *)((p) + dp_Arg4)))) -static char * -bstr(CPTR addr) +static char *bstr1 (uaecptr addr) { static char buf[256]; - int n = get_byte(addr++); int i; - for(i = 0; i < n; i++) - buf[i] = get_byte(addr++); + int n = get_byte(addr); + addr++; + + for (i = 0; i < n; i++, addr++) + buf[i] = get_byte(addr); buf[i] = 0; return buf; } -static Unit *units = NULL; +static char *bstr (Unit *unit, uaecptr addr) +{ + int i; + int n = get_byte(addr); + + addr++; + for (i = 0; i < n; i++, addr++) + unit->tmpbuf3[i] = get_byte(addr); + unit->tmpbuf3[i] = 0; + return unit->tmpbuf3; +} + +static char *bstr_cut (Unit *unit, uaecptr addr) +{ + char *p = unit->tmpbuf3; + int i, colon_seen = 0; + int n = get_byte (addr); + + addr++; + for (i = 0; i < n; i++, addr++) { + uae_u8 c = get_byte(addr); + unit->tmpbuf3[i] = c; + if (c == '/' || (c == ':' && colon_seen++ == 0)) + p = unit->tmpbuf3 + i + 1; + } + unit->tmpbuf3[i] = 0; + return p; +} + +static Unit *units = 0; static int unit_num = 0; static Unit* -find_unit(CPTR port) +find_unit (uaecptr port) { Unit* u; - for(u = units; u; u = u->next) - if(u->port == port) + for (u = units; u; u = u->next) + if (u->port == port) break; return u; } + +static void prepare_for_open (char *name) +{ +#if 0 + struct stat statbuf; + int mode; + + if (-1 == stat (name, &statbuf)) + return; + + mode = statbuf.st_mode; + mode |= S_IRUSR; + mode |= S_IWUSR; + mode |= S_IXUSR; + chmod (name, mode); +#endif +} -static CPTR DosAllocMem(ULONG len) +static void de_recycle_aino (Unit *unit, a_inode *aino) { - ULONG i; - CPTR addr; + if (aino->next == 0 || aino == &unit->rootnode) + return; + aino->next->prev = aino->prev; + aino->prev->next = aino->next; + aino->next = aino->prev = 0; + unit->aino_cache_size--; +} + +static void dispose_aino (Unit *unit, a_inode **aip, a_inode *aino) +{ + int hash = aino->uniq % MAX_AINO_HASH; + if (unit->aino_hash[hash] == aino) + unit->aino_hash[hash] = 0; + + if (aino->dirty && aino->parent) + fsdb_dir_writeback (aino->parent); + + *aip = aino->sibling; + if (aino->comment) + free (aino->comment); + free (aino->nname); + free (aino->aname); + free (aino); +} + +static void recycle_aino (Unit *unit, a_inode *aino) +{ + if (aino->dir || aino->shlock > 0 || aino->elock || aino == &unit->rootnode) + /* Still in use */ + return; + + /* Chain it into circular list. */ + aino->next = unit->rootnode.next; + aino->prev = &unit->rootnode; + aino->prev->next = aino; + aino->next->prev = aino; + unit->aino_cache_size++; + if (unit->aino_cache_size > 500) { + /* Reap a few. */ + int i = 0; + while (i < 50) { + a_inode **aip; + aip = &unit->rootnode.prev->parent->child; + for (;;) { + aino = *aip; + if (aino == 0) + break; + + if (aino->next == 0) + aip = &aino->sibling; + else { + if (aino->shlock > 0 || aino->elock) + write_log ("panic: freeing locked a_inode!\n"); + + de_recycle_aino (unit, aino); + dispose_aino (unit, aip, aino); + i++; + } + } + } +#if 0 + { + char buffer[40]; + sprintf (buffer, "%d ainos reaped.\n", i); + write_log (buffer); + } +#endif + } +} + +static void update_child_names (Unit *unit, a_inode *a, a_inode *parent) +{ + int l0 = strlen (parent->nname) + 2; + + while (a != 0) { + char *name_start; + char *new_name; + + a->parent = parent; + name_start = strrchr (a->nname, '/'); + if (name_start == 0) { + write_log ("malformed file name"); + } + name_start++; + new_name = (char *)xmalloc (strlen (name_start) + l0); + strcpy (new_name, parent->nname); + strcat (new_name, "/"); + strcat (new_name, name_start); + free (a->nname); + a->nname = new_name; + if (a->child) + update_child_names (unit, a->child, a); + a = a->sibling; + } +} + +static void move_aino_children (Unit *unit, a_inode *from, a_inode *to) +{ + to->child = from->child; + from->child = 0; + update_child_names (unit, to->child, to); +} + +static void delete_aino (Unit *unit, a_inode *aino) +{ + a_inode **aip; + int hash; + + TRACE(("deleting aino %x\n", aino->uniq)); + + aino->dirty = 1; + aino->deleted = 1; + de_recycle_aino (unit, aino); + aip = &aino->parent->child; + while (*aip != aino && *aip != 0) + aip = &(*aip)->sibling; + if (*aip != aino) { + write_log ("Couldn't delete aino.\n"); + return; + } + dispose_aino (unit, aip, aino); +} - regs.d[0] = len + 4; - regs.d[1] = 1; /* MEMF_PUBLIC */ - addr = CallLib(regs.a[6], -198); /* AllocMem */ +static a_inode *lookup_sub (a_inode *dir, uae_u32 uniq) +{ + a_inode **cp = &dir->child; + a_inode *c, *retval; - if(addr) { - put_long(addr, len); - addr += 4; + for (;;) { + c = *cp; + if (c == 0) + return 0; - /* faster to clear memory here rather than use MEMF_CLEAR */ - for(i = 0; i < len; i += 4) - put_long(addr + i, 0); + if (c->uniq == uniq) { + retval = c; + break; + } + if (c->dir) { + a_inode *a = lookup_sub (c, uniq); + if (a != 0) { + retval = a; + break; + } + } + cp = &c->sibling; } + *cp = c->sibling; + c->sibling = dir->child; + dir->child = c; + return retval; +} + +static a_inode *lookup_aino (Unit *unit, uae_u32 uniq) +{ + a_inode *a; + int hash = uniq % MAX_AINO_HASH; - return addr; + if (uniq == 0) + return &unit->rootnode; + a = unit->aino_hash[hash]; + if (a == 0 || a->uniq != uniq) + a = lookup_sub (&unit->rootnode, uniq); + else + unit->nr_cache_hits++; + unit->nr_cache_lookups++; + unit->aino_hash[hash] = a; + return a; } -static void DosFreeMem(CPTR addr) +char *build_nname (const char *d, const char *n) { - addr -= 4; - regs.d[0] = get_long(addr) + 4; - regs.a[1] = addr; - CallLib(regs.a[6], -210); /* FreeMem */ + char dsep[2] = { FSDB_DIR_SEPARATOR, '\0' }; + char *p = (char *) xmalloc (strlen (d) + strlen (n) + 2); + strcpy (p, d); + strcat (p, dsep); + strcat (p, n); + return p; } -static void -startup(DosPacket* packet) +char *build_aname (const char *d, const char *n) +{ + char *p = (char *) xmalloc (strlen (d) + strlen (n) + 2); + strcpy (p, d); + strcat (p, "/"); + strcat (p, n); + return p; +} + +/* This gets called to translate an Amiga name that some program used to + * a name that we can use on the native filesystem. */ +static char *get_nname (Unit *unit, a_inode *base, char *rel, + char **modified_rel) +{ + char *found; + char *p = 0; + + *modified_rel = 0; + + /* If we have a mapping of some other aname to "rel", we must pretend + * it does not exist. + * This can happen for example if an Amiga program creates a + * file called ".". We can't represent this in our filesystem, + * so we create a special file "uae_xxx" and record the mapping + * aname "." -> nname "uae_xxx" in the database. Then, the Amiga + * program looks up "uae_xxx" (yes, it's contrived). The filesystem + * should not make the uae_xxx file visible to the Amiga side. */ + if (fsdb_used_as_nname (base, rel)) + return 0; + /* A file called "." (or whatever else is invalid on this filesystem) + * does not exist, as far as the Amiga side is concerned. */ + if (fsdb_name_invalid (rel)) + return 0; + + /* See if we have a file that has the same name as the aname we are + * looking for. */ + found = fsdb_search_dir (base->nname, rel); + if (found == 0) + return found; + if (found == rel) + return build_nname (base->nname, rel); + + *modified_rel = found; + return build_nname (base->nname, found); +} + +static char *create_nname (Unit *unit, a_inode *base, char *rel) { + char *p; + + /* We are trying to create a file called REL. */ + + /* If the name is used otherwise in the directory (or globally), we + * need a new unique nname. */ + if (fsdb_name_invalid (rel) || fsdb_used_as_nname (base, rel)) { + oh_dear: + p = fsdb_create_unique_nname (base, rel); + return p; + } + p = build_nname (base->nname, rel); + + /* Delete this code once we know everything works. */ + if (access (p, R_OK) >= 0 || errno != ENOENT) { + write_log ("Filesystem in trouble... please report.\n"); + free (p); + goto oh_dear; + } + return p; +} + +/* + * This gets called if an ACTION_EXAMINE_NEXT happens and we hit an object + * for which we know the name on the native filesystem, but no corresponding + * Amiga filesystem name. + * @@@ For DOS filesystems, it might make sense to declare the new name + * "weak", so that it can get overriden by a subsequent call to get_nname(). + * That way, if someone does "dir :" and there is a file "foobar.inf", and + * someone else tries to open "foobar.info", get_nname() could maybe made to + * figure out that this is supposed to be the file "foobar.inf". + * DOS sucks... + */ +static char *get_aname (Unit *unit, a_inode *base, char *rel) +{ + return my_strdup (rel); +} + +static void init_child_aino (Unit *unit, a_inode *base, a_inode *aino) +{ + aino->uniq = ++unit->a_uniq; + if (unit->a_uniq == 0xFFFFFFFF) { + write_log ("Running out of a_inodes (prepare for big trouble)!\n"); + } + aino->shlock = 0; + aino->elock = 0; + + aino->dirty = 0; + aino->deleted = 0; + + /* Update tree structure */ + aino->parent = base; + aino->child = 0; + aino->sibling = base->child; + base->child = aino; + aino->next = aino->prev = 0; +} + +static a_inode *new_child_aino (Unit *unit, a_inode *base, char *rel) +{ + char *modified_rel; + char *nn; + a_inode *aino; + + TRACE(("new_child_aino %s, %s\n", base->aname, rel)); + + aino = fsdb_lookup_aino_aname (base, rel); + if (aino == 0) { + nn = get_nname (unit, base, rel, &modified_rel); + if (nn == 0) + return 0; + + aino = (a_inode *) xmalloc (sizeof (a_inode)); + if (aino == 0) + return 0; + aino->aname = modified_rel ? modified_rel : my_strdup (rel); + aino->nname = nn; + + aino->comment = 0; + aino->has_dbentry = 0; + + fsdb_fill_file_attrs (aino); + if (aino->dir) + fsdb_clean_dir (aino); + } + init_child_aino (unit, base, aino); + + recycle_aino (unit, aino); + TRACE(("created aino %x, lookup\n", aino->uniq)); + return aino; +} + +static a_inode *create_child_aino (Unit *unit, a_inode *base, char *rel, int isdir) +{ + a_inode *aino = (a_inode *) xmalloc (sizeof (a_inode)); + if (aino == 0) + return 0; + + aino->aname = my_strdup (rel); + aino->nname = create_nname (unit, base, rel); + + init_child_aino (unit, base, aino); + aino->amigaos_mode = 0; + aino->dir = isdir; + + aino->comment = 0; + aino->has_dbentry = 0; + aino->dirty = 1; + + recycle_aino (unit, aino); + TRACE(("created aino %x, create\n", aino->uniq)); + return aino; +} + +static a_inode *lookup_child_aino (Unit *unit, a_inode *base, char *rel, uae_u32 *err) +{ + a_inode *c = base->child; + int l0 = strlen (rel); + + if (base->dir == 0) { + *err = ERROR_OBJECT_WRONG_TYPE; + return 0; + } + + while (c != 0) { + int l1 = strlen (c->aname); + if (l0 <= l1 && same_aname (rel, c->aname + l1 - l0) + && (l0 == l1 || c->aname[l1-l0-1] == '/')) + break; + c = c->sibling; + } + if (c != 0) + return c; + c = new_child_aino (unit, base, rel); + if (c == 0) + *err = ERROR_OBJECT_NOT_FOUND; + return c; +} + +/* Different version because for this one, REL is an nname. */ +static a_inode *lookup_child_aino_for_exnext (Unit *unit, a_inode *base, char *rel, uae_u32 *err) +{ + a_inode *c = base->child; + int l0 = strlen (rel); + + *err = 0; + while (c != 0) { + int l1 = strlen (c->nname); + /* Note: using strcmp here. */ + if (l0 <= l1 && strcmp (rel, c->nname + l1 - l0) == 0 + && (l0 == l1 || c->nname[l1-l0-1] == FSDB_DIR_SEPARATOR)) + break; + c = c->sibling; + } + if (c != 0) + return c; + c = fsdb_lookup_aino_nname (base, rel); + if (c == 0) { + c = (a_inode *)malloc (sizeof (a_inode)); + if (c == 0) { + *err = ERROR_NO_FREE_STORE; + return 0; + } + + c->nname = build_nname (base->nname, rel); + c->aname = get_aname (unit, base, rel); + c->comment = 0; + c->has_dbentry = 0; + fsdb_fill_file_attrs (c); + if (c->dir) + fsdb_clean_dir (c); + } + init_child_aino (unit, base, c); + + recycle_aino (unit, c); + TRACE(("created aino %x, exnext\n", c->uniq)); + + return c; +} + +static a_inode *get_aino (Unit *unit, a_inode *base, const char *rel, uae_u32 *err) +{ + char *tmp; + char *p; + a_inode *curr; + int i; + + *err = 0; + TRACE(("get_path(%s,%s)\n", base->aname, rel)); + + /* root-relative path? */ + for (i = 0; rel[i] && rel[i] != '/' && rel[i] != ':'; i++) + ; + if (':' == rel[i]) + rel += i+1; + + tmp = my_strdup (rel); + p = tmp; + curr = base; + + while (*p) { + /* start with a slash? go up a level. */ + if (*p == '/') { + if (curr->parent != 0) + curr = curr->parent; + p++; + } else { + a_inode *next; + + char *component_end; + component_end = strchr (p, '/'); + if (component_end != 0) + *component_end = '\0'; + next = lookup_child_aino (unit, curr, p, err); + if (next == 0) { + /* if only last component not found, return parent dir. */ + if (*err != ERROR_OBJECT_NOT_FOUND || component_end != 0) + curr = 0; + /* ? what error is appropriate? */ + break; + } + curr = next; + if (component_end) + p = component_end+1; + else + break; + + } + } + free (tmp); + return curr; +} + +static uae_u32 startup_handler (void) +{ + /* Just got the startup packet. It's in A4. DosBase is in A2, + * our allocated volume structure is in D6, A5 is a pointer to + * our port. */ + uaecptr rootnode = get_long (m68k_areg (regs, 2) + 34); + uaecptr dos_info = get_long (rootnode + 24) << 2; + uaecptr pkt = m68k_dreg (regs, 3); + uaecptr arg2 = get_long (pkt + dp_Arg2); int i, namelen; - char* devname = bstr(packet->arg1 << 2); + char* devname = bstr1 (get_long (pkt + dp_Arg1) << 2); char* s; - Unit* unit; + Unit *unit; + UnitInfo *uinfo; /* find UnitInfo with correct device name */ - s = strchr(devname, ':'); - if(s) *s = '\0'; + s = strchr (devname, ':'); + if (s) + *s = '\0'; - for(i = 0; i < num_units; i++) { + for (i = 0; i < current_mountinfo->num_units; i++) { /* Hardfile volume name? */ - if (ui[i].volname == NULL) + if (current_mountinfo->ui[i].volname == 0) continue; - - if (ui[i].startup == packet->arg2) - break; + + if (current_mountinfo->ui[i].startup == arg2) + break; } - - if(i == num_units || 0 != access(ui[i].rootdir, R_OK)) { - fprintf(stderr, "Failed attempt to mount device\n", devname); - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DEVICE_NOT_MOUNTED; - return; + + if (i == current_mountinfo->num_units + || access (current_mountinfo->ui[i].rootdir, R_OK) != 0) + { + write_log ("Failed attempt to mount device\n", devname); + put_long (pkt + dp_Res1, DOS_FALSE); + put_long (pkt + dp_Res2, ERROR_DEVICE_NOT_MOUNTED); + return 1; } + uinfo = current_mountinfo->ui + i; - unit = (Unit *) malloc(sizeof(Unit)); + unit = (Unit *) xmalloc (sizeof (Unit)); unit->next = units; units = unit; + uinfo->self = unit; unit->volume = 0; - unit->port = regs.a[5]; + unit->port = m68k_areg (regs, 5); unit->unit = unit_num++; - unit->ui.devname = ui[i].devname; - unit->ui.volname = my_strdup(ui[i].volname); /* might free later for rename */ - unit->ui.rootdir = ui[i].rootdir; - unit->ui.readonly = ui[i].readonly; + unit->ui.devname = uinfo->devname; + unit->ui.volname = my_strdup (uinfo->volname); /* might free later for rename */ + unit->ui.rootdir = uinfo->rootdir; + unit->ui.readonly = uinfo->readonly; + unit->ui.unit_pipe = uinfo->unit_pipe; + unit->ui.back_pipe = uinfo->back_pipe; + unit->cmds_complete = 0; + unit->cmds_sent = 0; + unit->cmds_acked = 0; + for (i = 0; i < EXKEYS; i++) { + unit->examine_keys[i].aino = 0; + unit->examine_keys[i].dir = 0; + unit->examine_keys[i].uniq = 0; + } + unit->next_exkey = 1; + unit->keys = 0; + unit->a_uniq = unit->key_uniq = 0; + + unit->rootnode.aname = uinfo->volname; + unit->rootnode.nname = uinfo->rootdir; + unit->rootnode.sibling = 0; + unit->rootnode.next = unit->rootnode.prev = &unit->rootnode; + unit->rootnode.uniq = 0; + unit->rootnode.parent = 0; + unit->rootnode.child = 0; + unit->rootnode.dir = 1; + unit->rootnode.amigaos_mode = 0; + unit->rootnode.shlock = 0; + unit->rootnode.elock = 0; + unit->aino_cache_size = 0; + for (i = 0; i < MAX_AINO_HASH; i++) + unit->aino_hash[i] = 0; + +/* write_comm_pipe_int (unit->ui.unit_pipe, -1, 1);*/ TRACE(("**** STARTUP volume %s\n", unit->ui.volname)); /* fill in our process in the device node */ - put_long((packet->arg3 << 2) + 8, unit->port); + put_long ((get_long (pkt + dp_Arg3) << 2) + 8, unit->port); + unit->dosbase = m68k_areg (regs, 2); - /* open dos.library */ - regs.d[0] = 0; - regs.a[1] = EXPANSION_doslibname; - unit->dosbase = CallLib(regs.a[6], -552); /* OpenLibrary */ + /* make new volume */ + unit->volume = m68k_areg (regs, 3) + 32; +#ifdef UAE_FILESYS_THREADS + unit->locklist = m68k_areg (regs, 3) + 8; +#else + unit->locklist = m68k_areg (regs, 3); +#endif + unit->dummy_message = m68k_areg (regs, 3) + 12; - { - CPTR rootnode = get_long(unit->dosbase + 34); - CPTR dos_info = get_long(rootnode + 24) << 2; - /* make new volume */ - unit->volume = DosAllocMem(80 + 1 + 44); - put_long(unit->volume + 4, 2); /* Type = dt_volume */ - put_long(unit->volume + 12, 0); /* Lock */ - put_long(unit->volume + 16, 3800); /* Creation Date */ - put_long(unit->volume + 20, 0); - put_long(unit->volume + 24, 0); - put_long(unit->volume + 28, 0); /* lock list */ - put_long(unit->volume + 40, (unit->volume + 44) >> 2); /* Name */ - namelen = strlen(unit->ui.volname); - put_byte(unit->volume + 44, namelen); - for(i = 0; i < namelen; i++) - put_byte(unit->volume + 45 + i, unit->ui.volname[i]); - - /* link into DOS list */ - put_long(unit->volume, get_long(dos_info + 4)); - put_long(dos_info + 4, unit->volume >> 2); - } - - put_long(unit->volume + 8, unit->port); - put_long(unit->volume + 32, DISK_TYPE); + put_long (unit->dummy_message + 10, 0); + + put_long (unit->volume + 4, 2); /* Type = dt_volume */ + put_long (unit->volume + 12, 0); /* Lock */ + put_long (unit->volume + 16, 3800); /* Creation Date */ + put_long (unit->volume + 20, 0); + put_long (unit->volume + 24, 0); + put_long (unit->volume + 28, 0); /* lock list */ + put_long (unit->volume + 40, (unit->volume + 44) >> 2); /* Name */ + namelen = strlen (unit->ui.volname); + put_byte (unit->volume + 44, namelen); + for (i = 0; i < namelen; i++) + put_byte (unit->volume + 45 + i, unit->ui.volname[i]); + + /* link into DOS list */ + put_long (unit->volume, get_long (dos_info + 4)); + put_long (dos_info + 4, unit->volume >> 2); - packet->res1 = DOS_TRUE; + put_long (unit->volume + 8, unit->port); + put_long (unit->volume + 32, DISK_TYPE); + + put_long (pkt + dp_Res1, DOS_TRUE); + + fsdb_clean_dir (&unit->rootnode); + + return 0; } -#ifdef HAVE_STATFS static void -do_info(Unit* unit, DosPacket* packet, CPTR info) +do_info (Unit *unit, dpacket packet, uaecptr info) { - struct statfs statbuf; -#if STATFS_NO_ARGS == 2 - if(-1 == statfs(unit->ui.rootdir, &statbuf)) -#else - if(-1 == statfs(unit->ui.rootdir, &statbuf, sizeof(struct statfs), 0)) -#endif - { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); + struct fs_usage fsu; + + if (get_fs_usage (unit->ui.rootdir, 0, &fsu) != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno ()); + return; } - put_long(info, 0); /* errors */ - put_long(info + 4, unit->unit); /* unit number */ - put_long(info + 8, unit->ui.readonly ? 80 : 82); /* state */ - put_long(info + 12, statbuf.f_blocks); /* numblocks */ - put_long(info + 16, statbuf.f_blocks - statbuf.STATBUF_BAVAIL); /* inuse */ - put_long(info + 20, statbuf.f_bsize); /* bytesperblock */ - put_long(info + 24, DISK_TYPE); /* disk type */ - put_long(info + 28, unit->volume >> 2); /* volume node */ - put_long(info + 32, 0); /* inuse */ - packet->res1 = DOS_TRUE; -} -#else -static void -do_info(Unit* unit, DosPacket* packet, CPTR info) -{ - put_long(info, 0); /* errors */ - put_long(info + 4, unit->unit); /* unit number */ - put_long(info + 8, unit->ui.readonly ? 80 : 82); /* state */ - put_long(info + 12, 256); /* numblocks */ - put_long(info + 16, 128); /* inuse */ - put_long(info + 20, 512); /* bytesperblock */ - put_long(info + 24, DISK_TYPE); /* disk type */ - put_long(info + 28, unit->volume >> 2); /* volume node */ - put_long(info + 32, 0); /* inuse */ - packet->res1 = DOS_TRUE; + fsu.fsu_blocks >>= 1; + fsu.fsu_bavail >>= 1; + put_long (info, 0); /* errors */ + put_long (info + 4, unit->unit); /* unit number */ + put_long (info + 8, unit->ui.readonly ? 80 : 82); /* state */ + put_long (info + 12, fsu.fsu_blocks ); /* numblocks */ + put_long (info + 16, fsu.fsu_blocks - fsu.fsu_bavail); /* inuse */ + put_long (info + 20, 1024); /* bytesperblock */ + put_long (info + 24, DISK_TYPE); /* disk type */ + put_long (info + 28, unit->volume >> 2); /* volume node */ + put_long (info + 32, 0); /* inuse */ + PUT_PCK_RES1 (packet, DOS_TRUE); } -#endif static void -action_disk_info(Unit* unit, DosPacket* packet) +action_disk_info (Unit *unit, dpacket packet) { TRACE(("ACTION_DISK_INFO\n")); - do_info(unit, packet, packet->arg1 << 2); + do_info(unit, packet, GET_PCK_ARG1 (packet) << 2); } static void -action_info(Unit* unit, DosPacket* packet) +action_info (Unit *unit, dpacket packet) { TRACE(("ACTION_INFO\n")); - do_info(unit, packet, packet->arg2 << 2); + do_info(unit, packet, GET_PCK_ARG2 (packet) << 2); } -typedef struct key { - struct key *next; - ULONG uniq; - char *path; - int fd; - off_t file_pos; -} Key; - -static struct key* keys = NULL; - -static void -free_key(Key*k) +static void free_key (Unit *unit, Key *k) { Key *k1; - Key *prev = NULL; - for(k1 = keys; k1; k1 = k1->next) { - if(k == k1) { - if(prev) - prev->next = k->next; - else - keys = k->next; - break; - } - prev = k1; + Key *prev = 0; + for (k1 = unit->keys; k1; k1 = k1->next) { + if (k == k1) { + if (prev) + prev->next = k->next; + else + unit->keys = k->next; + break; + } + prev = k1; } if (k->fd >= 0) close(k->fd); - free(k->path); + free(k); } -static Key* -lookup_key(ULONG uniq) +static Key *lookup_key (Unit *unit, uae_u32 uniq) { Key *k; - for(k = keys; k; k = k->next) { - if(uniq == k->uniq) - return k; + /* It's hardly worthwhile to optimize this - most of the time there are + * only one or zero keys. */ + for (k = unit->keys; k; k = k->next) { + if (uniq == k->uniq) + return k; } - fprintf(stderr, "Error: couldn't find key %ld\n", uniq); + write_log ("Error: couldn't find key!\n"); +#if 0 exit(1); /* NOTREACHED */ - return NULL; +#endif + /* There isn't much hope we will recover. Unix would kill the process, + * AmigaOS gets killed by it. */ + write_log ("Better reset that Amiga - the system is messed up.\n"); + return 0; } -static Key* -new_key(void) +static Key *new_key (Unit *unit) { - static ULONG uniq = 0; - Key *k = (Key*) malloc(sizeof(Key)); - k->uniq = ++uniq; + Key *k = (Key *) xmalloc(sizeof(Key)); + k->uniq = ++unit->key_uniq; k->fd = -1; k->file_pos = 0; - k->next = keys; - keys = k; + k->next = unit->keys; + unit->keys = k; return k; } static void -dumplock(CPTR lock) +dumplock (Unit *unit, uaecptr lock) { - if(!lock) { - fprintf(stderr, "LOCK: 0x0\n"); + a_inode *a; + TRACE(("LOCK: 0x%lx", lock)); + if (!lock) { + TRACE(("\n")); return; } - fprintf(stderr, - "LOCK: 0x%lx { next=0x%lx, key=%s, mode=%ld, handler=0x%lx, volume=0x%lx }\n", - lock, - get_long(lock)<<2, lookup_key(get_long(lock+4))->path, get_long(lock+8), - get_long(lock+12), get_long(lock+16)); + TRACE(("{ next=0x%lx, mode=%ld, handler=0x%lx, volume=0x%lx, aino %lx ", + get_long (lock) << 2, get_long (lock+8), + get_long (lock+12), get_long (lock+16), + get_long (lock + 4))); + a = lookup_aino (unit, get_long (lock + 4)); + if (a == 0) { + TRACE(("not found!")); + } else { + TRACE(("%s", a->nname)); + } + TRACE((" }\n")); } -static char* -get_path(Unit* unit, const char *base, const char *rel) +static a_inode *find_aino (Unit *unit, uaecptr lock, const char *name, uae_u32 *err) { - static char buf[1024]; - char *s = buf; - char *p; - char *r; - - int i; - - TRACE(("get_path(%s,%s)\n", base, rel)); + a_inode *a; - /* root-relative path? */ - for(i = 0; rel[i] && rel[i] != '/' && rel[i] != ':'; i++); - if(':' == rel[i]) { - /*base = unit->ui.rootdir;*/ rel += i+1; - } - - while(*base) { - *s++ = *base++; - } - *s = 0; - p = s; /* start of relative path */ - r = buf + strlen(unit->ui.rootdir); /* end of fixed path */ - - while(*rel) { - /* start with a slash? go up a level. */ - if('/' == *rel) { - while(s > r && '/' != *s) - s--; - *s = 0; - rel++; + if (lock) { + a_inode *olda = lookup_aino (unit, get_long (lock + 4)); + if (olda == 0) { + /* That's the best we can hope to do. */ + a = get_aino (unit, &unit->rootnode, name, err); } else { - *s++ = '/'; - while(*rel && '/' != *rel) { - *s++ = *rel++; - } - *s = 0; - if('/' == *rel) - rel++; - } - } - *s = 0; - -#ifdef MAKE_CASE_INSENSITIVE - TRACE(("path=\"%s\"\n", buf)); - /* go through each section of the path and if it does not exist, - * scan its directory for any case insensitive matches - */ - while(*p) { - char *p2 = strchr(p+1, '/'); - char oldp2; - if(!p2) { - p2 = p+1; - while(*p2) p2++; - } - oldp2 = *p2; - *p2 = '\0'; - if(0 != access(buf, F_OK|R_OK)) { - DIR* dir; - struct dirent* de; - /* does not exist -- check dir for case insensitive match */ - *p++ = '\0'; /* was '/' */ - dir = opendir(buf); - if (dir) { - while((de = readdir(dir))) { -#if 0 - if(0 == stricmp(de->d_name, p)) /* OLSEN */ -#endif - if(0 == strcasecmp(de->d_name, p)) - break; - } - if(de) { - strcpy(p, de->d_name); - } - closedir(dir); - } - *--p = '/'; + TRACE(("aino: 0x%08lx", (unsigned long int)olda->uniq)); + TRACE((" \"%s\"\n", olda->nname)); + a = get_aino (unit, olda, name, err); } - *p2 = oldp2; - p = p2; - } -#endif - TRACE(("path=\"%s\"\n", buf)); - - return my_strdup(buf); -} - -static Key* -make_key(Unit* unit, CPTR lock, const char *name) -{ - Key *k = new_key(); - - if(!lock) { - k->path = get_path(unit, unit->ui.rootdir, name); } else { - Key*oldk = lookup_key(get_long(lock + 4)); -#if 0 - const char *nm = strchr (name, ':'); - if (nm == NULL) { - nm = name; - } else - nm++; -#else - const char *nm = name; -#endif - TRACE(("key: 0x%08lx", oldk->uniq)); - TRACE((" \"%s\"\n", oldk->path)); - k->path = get_path(unit, oldk->path, nm); + a = get_aino (unit, &unit->rootnode, name, err); } - - TRACE(("key=\"%s\"\n", k->path)); - return k; + if (a) { + TRACE(("aino=\"%s\"\n", a->nname)); + } + return a; } -static Key* -dup_key(Key*k) +static uaecptr make_lock (Unit *unit, uae_u32 uniq, long mode) { - Key *newk = new_key(); - newk->path = my_strdup(k->path); - return newk; -} + /* allocate lock from the list kept by the assembly code */ + uaecptr lock; -static CPTR -make_lock(Unit* unit, Key *key, long mode) -{ - /* allocate lock */ - CPTR lock = DosAllocMem(20); + lock = get_long (unit->locklist); + put_long (unit->locklist, get_long (lock)); + lock += 4; - put_long(lock + 4, key->uniq); - put_long(lock + 8, mode); - put_long(lock + 12, unit->port); - put_long(lock + 16, unit->volume >> 2); + put_long (lock + 4, uniq); + put_long (lock + 8, mode); + put_long (lock + 12, unit->port); + put_long (lock + 16, unit->volume >> 2); /* prepend to lock chain */ - put_long(lock, get_long(unit->volume + 28)); - put_long(unit->volume + 28, lock >> 2); + put_long (lock, get_long (unit->volume + 28)); + put_long (unit->volume + 28, lock >> 2); - DUMPLOCK(lock); + DUMPLOCK(unit, lock); return lock; } -static void -free_lock(Unit* unit, CPTR lock) +static void free_lock (Unit *unit, uaecptr lock) { - if(!lock) + if (! lock) return; - if(lock == get_long(unit->volume + 28) << 2) { - put_long(unit->volume + 28, get_long(lock)); + if (lock == get_long (unit->volume + 28) << 2) { + put_long (unit->volume + 28, get_long (lock)); } else { - CPTR current = get_long(unit->volume + 28); - CPTR next = 0; - while(current) { - next = get_long(current << 2); - if(lock == next << 2) + uaecptr current = get_long (unit->volume + 28); + uaecptr next = 0; + while (current) { + next = get_long (current << 2); + if (lock == next << 2) break; current = next; } - put_long(current << 2, get_long(lock)); + put_long (current << 2, get_long (lock)); } - free_key(lookup_key(get_long(lock + 4))); - DosFreeMem(lock); + lock -= 4; + put_long (lock, get_long (unit->locklist)); + put_long (unit->locklist, lock); } static void -action_lock(Unit* unit, DosPacket* packet) +action_lock (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - CPTR name = packet->arg2 << 2; - long mode = packet->arg3; - int access_mode = (mode == -2) ? R_OK : R_OK|W_OK; - Key *k; + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + uaecptr name = GET_PCK_ARG2 (packet) << 2; + long mode = GET_PCK_ARG3 (packet); + a_inode *a; + uae_u32 err; - TRACE(("ACTION_LOCK(0x%lx, \"%s\", %d)\n",lock, bstr(name), mode)); - DUMPLOCK(lock); + if (mode != -2 && mode != -1) { + TRACE(("Bad mode.\n")); + mode = -2; + } - k = make_key(unit, lock, bstr(name)); + TRACE(("ACTION_LOCK(0x%lx, \"%s\", %d)\n", lock, bstr (unit, name), mode)); + DUMPLOCK(unit, lock); - if(k && 0 == access(k->path, access_mode)) { - packet->res1 = make_lock(unit, k, mode) >> 2; - } else { - if(k) - free_key(k); - packet->res1 = 0; - packet->res2 = ERROR_OBJECT_NOT_FOUND; + a = find_aino (unit, lock, bstr (unit, name), &err); + if (err == 0 && (a->elock || (mode != -2 && a->shlock > 0))) { + err = ERROR_OBJECT_IN_USE; } + /* Lock() doesn't do access checks. */ + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } + if (mode == -2) + a->shlock++; + else + a->elock = 1; + de_recycle_aino (unit, a); + PUT_PCK_RES1 (packet, make_lock (unit, a->uniq, mode) >> 2); } -static void -action_free_lock(Unit* unit, DosPacket* packet) +static void action_free_lock (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + a_inode *a; TRACE(("ACTION_FREE_LOCK(0x%lx)\n", lock)); - DUMPLOCK(lock); + DUMPLOCK(unit, lock); + a = lookup_aino (unit, get_long (lock + 4)); + if (a == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_FOUND); + return; + } + if (a->elock) + a->elock = 0; + else + a->shlock--; + recycle_aino (unit, a); free_lock(unit, lock); - packet->res1 = DOS_TRUE; + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_dup_lock(Unit* unit, DosPacket* packet) +action_dup_lock (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + a_inode *a; TRACE(("ACTION_DUP_LOCK(0x%lx)\n", lock)); - DUMPLOCK(lock); + DUMPLOCK(unit, lock); - if(!lock) { - packet->res1 = 0; + if (!lock) { + PUT_PCK_RES1 (packet, 0); return; } - - { - CPTR oldkey = get_long(lock + 4); - CPTR oldmode = get_long(lock + 8); - packet->res1 = make_lock(unit, dup_key(lookup_key(oldkey)), oldmode) >> 2; + a = lookup_aino (unit, get_long (lock + 4)); + if (a == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_FOUND); + return; } + /* DupLock()ing exclusive locks isn't possible, says the Autodoc, but + * at least the RAM-Handler seems to allow it. Let's see what happens + * if we don't. */ + if (a->elock) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_IN_USE); + return; + } + a->shlock++; + de_recycle_aino (unit, a); + PUT_PCK_RES1 (packet, make_lock (unit, a->uniq, -2) >> 2); } /* convert time_t to/from AmigaDOS time */ @@ -724,7 +1473,7 @@ const int secs_per_day = 24 * 60 * 60; const int diff = (8 * 365 + 2) * (24 * 60 * 60); static void -get_time(time_t t, long* days, long* mins, long* ticks) +get_time (time_t t, long* days, long* mins, long* ticks) { /* time_t is secs since 1-1-1970 */ /* days since 1-1-1978 */ @@ -740,7 +1489,7 @@ get_time(time_t t, long* days, long* min } static time_t -put_time(long days, long mins, long ticks) +put_time (long days, long mins, long ticks) { time_t t; t = ticks / 50; @@ -751,269 +1500,455 @@ put_time(long days, long mins, long tick return t; } - -typedef struct { - ULONG uniq; - char *path; - DIR* dir; -} ExamineKey; - -/* Since ACTION_EXAMINE_NEXT is so braindamaged, we have to keep - * some of these around - */ - -#define EXKEYS 100 -static ExamineKey examine_keys[EXKEYS]; -static int next_exkey = 0; - -static void -free_exkey(ExamineKey* ek) +static void free_exkey (ExamineKey *ek) { - free(ek->path); - ek->path = 0; - if(ek->dir) - closedir(ek->dir); + ek->aino = 0; + ek->uniq = 0; + if (ek->dir) + closedir (ek->dir); } -static ExamineKey* -new_exkey(char *path) +/* This is so sick... who invented ACTION_EXAMINE_NEXT? What did he THINK??? */ +static ExamineKey *new_exkey (Unit *unit, a_inode *aino) { - ULONG uniq = next_exkey; - ExamineKey* ek= &examine_keys[next_exkey++]; - if(next_exkey==EXKEYS) - next_exkey = 0; - if(ek->path) { - free_exkey(ek); + uae_u32 uniq; + uae_u32 oldest = 0xFFFFFFFF; + ExamineKey *ek, *oldest_ek = 0; + int i; + + ek = unit->examine_keys; + for (i = 0; i < EXKEYS; i++, ek++) { + /* Did we find a free one? */ + if (ek->aino == 0) + continue; + if (ek->uniq < oldest) + oldest = (oldest_ek = ek)->uniq; + } + ek = unit->examine_keys; + for (i = 0; i < EXKEYS; i++, ek++) { + /* Did we find a free one? */ + if (ek->aino == 0) + goto found; + } + /* This message should usually be harmless. */ + write_log ("Houston, we have a problem.\n"); + free_exkey (oldest_ek); + ek = oldest_ek; + found: + + uniq = unit->next_exkey; + if (uniq == 0xFFFFFFFF) { + /* Things will probably go wrong, but most likely the Amiga will crash + * before this happens because of something else. */ + uniq = 1; } - ek->path = my_strdup(path); + unit->next_exkey = uniq+1; + ek->aino = aino; ek->dir = 0; ek->uniq = uniq; return ek; } +static ExamineKey *lookup_exkey (Unit *unit, uae_u32 uniq) +{ + ExamineKey *ek; + int i; + + ek = unit->examine_keys; + for (i = 0; i < EXKEYS; i++, ek++) { + /* Did we find a free one? */ + if (ek->uniq == uniq) + return ek; + } + write_log ("Houston, we have a BIG problem.\n"); + return 0; +} + static void -get_fileinfo(Unit *unit, DosPacket* packet, CPTR info, char *buf) +get_fileinfo (Unit *unit, dpacket packet, uaecptr info, a_inode *aino) { struct stat statbuf; long days, mins, ticks; + int i, n; + char *x; + + /* No error checks - this had better work. */ + stat (aino->nname, &statbuf); - if(-1 == stat(buf, &statbuf)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); + if (aino->parent == 0) { + x = unit->ui.volname; + put_long (info + 4, 1); + put_long (info + 120, 1); } else { - put_long(info + 4, S_ISDIR(statbuf.st_mode) ? 2 : -3); - { - /* file name */ - int i = 8; - int n; - char *x; - if (strcmp (buf, unit->ui.rootdir) == 0) { - x = unit->ui.volname; - } else { - x = strrchr(buf,'/'); - if(x) - x++; - else - x = buf; - } - TRACE(("name=\"%s\"\n", x)); - n = strlen(x); - if(n > 106) n = 106; - put_byte(info + i++, n); - while(n--) - put_byte(info + i++, *x++); - while(i < 108) - put_byte(info + i++, 0); - } - put_long(info + 116, - (S_IRUSR & statbuf.st_mode ? 0 : (1<<3)) | - (S_IWUSR & statbuf.st_mode ? 0 : (1<<2)) | -#ifndef __DOS__ - (S_IXUSR & statbuf.st_mode ? 0 : (1<<1)) | -#endif - (S_IWUSR & statbuf.st_mode ? 0 : (1<<0))); - put_long(info + 120, S_ISDIR(statbuf.st_mode) ? 2 : -3); - put_long(info + 124, statbuf.st_size); + /* AmigaOS docs say these have to contain the same value. */ + put_long (info + 4, aino->dir ? 2 : -3); + put_long (info + 120, aino->dir ? 2 : -3); + x = aino->aname; + } + TRACE(("name=\"%s\"\n", x)); + n = strlen (x); + if (n > 106) + n = 106; + i = 8; + put_byte (info + i, n); i++; + while (n--) + put_byte (info + i, *x), i++, x++; + while (i < 108) + put_byte (info + i, 0), i++; + + put_long (info + 116, aino->amigaos_mode); + put_long (info + 124, statbuf.st_size); #ifdef HAVE_ST_BLOCKS - put_long(info + 128, statbuf.st_blocks); + put_long (info + 128, statbuf.st_blocks); #else - put_long(info + 128, statbuf.st_size / 512 + 1); + put_long (info + 128, statbuf.st_size / 512 + 1); #endif - get_time(statbuf.st_mtime, &days, &mins, &ticks); - put_long(info + 132, days); - put_long(info + 136, mins); - put_long(info + 140, ticks); - put_long(info + 144, 0); /* no comment */ - packet->res1 = DOS_TRUE; + get_time (statbuf.st_mtime, &days, &mins, &ticks); + put_long (info + 132, days); + put_long (info + 136, mins); + put_long (info + 140, ticks); + if (aino->comment == 0) + put_long (info + 144, 0); + else { + TRACE(("comment=\"%s\"\n", aino->comment)); + i = 144; + n = strlen (x = aino->comment); + if (n > 78) + n = 78; + put_byte (info + i, n); i++; + while (n--) + put_byte (info + i, *x), i++, x++; + while (i < 224) + put_byte (info + i, 0), i++; } + PUT_PCK_RES1 (packet, DOS_TRUE); } -static void -do_examine(Unit *unit, DosPacket* packet, ExamineKey* ek, CPTR info) +static void do_examine (Unit *unit, dpacket packet, ExamineKey *ek, uaecptr info) { - static char buf[1024]; + struct dirent de_space; struct dirent* de; + a_inode *aino; + uae_u32 err; - if(!ek->dir) { - ek->dir = opendir(ek->path); + if (!ek->dir) { + ek->dir = opendir (ek->aino->nname); } - if(!ek->dir) { - free_exkey(ek); - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_MORE_ENTRIES; + if (!ek->dir) { + free_exkey (ek); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES); return; } - de = readdir(ek->dir); + do { + de = my_readdir (ek->dir, &de_space); + } while (de && fsdb_name_invalid (de->d_name)); - while(de && (0 == strcmp(".", de->d_name) - || 0 == strcmp("..", de->d_name))) - { - de = readdir(ek->dir); - } - - if(!de) { + if (!de) { TRACE(("no more entries\n")); - free_exkey(ek); - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_MORE_ENTRIES; + free_exkey (ek); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES); return; } TRACE(("entry=\"%s\"\n", de->d_name)); + aino = lookup_child_aino_for_exnext (unit, ek->aino, de->d_name, &err); + if (err != 0) { + write_log ("Severe problem in ExNext.\n"); + free_exkey (ek); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } + get_fileinfo (unit, packet, info, aino); +} - sprintf(buf, "%s/%s", ek->path, de->d_name); +static void action_examine_object (Unit *unit, dpacket packet) +{ + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + uaecptr info = GET_PCK_ARG2 (packet) << 2; + a_inode *aino = 0; + + TRACE(("ACTION_EXAMINE_OBJECT(0x%lx,0x%lx)\n", lock, info)); + DUMPLOCK(unit, lock); - get_fileinfo(unit, packet, info, buf); + if (lock != 0) + aino = lookup_aino (unit, get_long (lock + 4)); + if (aino == 0) + aino = &unit->rootnode; + + get_fileinfo (unit, packet, info, aino); + if (aino->dir) { + put_long (info, 0xFFFFFFFF); + } else + put_long (info, 0); } -static void -action_examine_object(Unit* unit, DosPacket* packet) +static void action_examine_next (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - CPTR info = packet->arg2 << 2; - char *path; - ExamineKey* ek; + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + uaecptr info = GET_PCK_ARG2 (packet) << 2; + a_inode *aino = 0; + ExamineKey *ek; + uae_u32 uniq; - TRACE(("ACTION_EXAMINE_OBJECT(0x%lx,0x%lx)\n", lock, info)); - DUMPLOCK(lock); + TRACE(("ACTION_EXAMINE_NEXT(0x%lx,0x%lx)\n", lock, info)); + DUMPLOCK(unit, lock); - if(!lock) { - path = unit->ui.rootdir; - } else { - Key*k = lookup_key(get_long(lock + 4)); - path = k->path; + if (lock != 0) + aino = lookup_aino (unit, get_long (lock + 4)); + if (aino == 0) + aino = &unit->rootnode; + + uniq = get_long (info); + if (uniq == 0) { + write_log ("ExNext called for a file! (Houston?)\n"); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES); + return; + } else if (uniq == 0xFFFFFFFF) { + ek = new_exkey(unit, aino); + } else + ek = lookup_exkey (unit, get_long (info)); + if (ek == 0) { + write_log ("Couldn't find a matching ExKey. Prepare for trouble.\n"); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES); + return; } - - get_fileinfo(unit, packet, info, path); - ek = new_exkey(path); - put_long(info, ek->uniq); + put_long (info, ek->uniq); + do_examine (unit, packet, ek, info); } -static void -action_examine_next(Unit* unit, DosPacket* packet) +static void do_find (Unit *unit, dpacket packet, int mode, int create, int fallback) { - CPTR lock = packet->arg1 << 2; - CPTR info = packet->arg2 << 2; + uaecptr fh = GET_PCK_ARG1 (packet) << 2; + uaecptr lock = GET_PCK_ARG2 (packet) << 2; + uaecptr name = GET_PCK_ARG3 (packet) << 2; + a_inode *aino; + Key *k; + int fd; + uae_u32 err; + mode_t openmode; + int aino_created = 0; + + TRACE(("ACTION_FIND_*(0x%lx,0x%lx,\"%s\",%d)\n", fh, lock, bstr (unit, name), mode)); + DUMPLOCK(unit, lock); + + aino = find_aino (unit, lock, bstr (unit, name), &err); + + if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_FOUND)) { + /* Whatever it is, we can't handle it. */ + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } + if (err == 0) { + /* Object exists. */ + if (aino->dir) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_WRONG_TYPE); + return; + } + if (aino->elock || (create == 2 && aino->shlock > 0)) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_IN_USE); + return; + } + if (create == 2 && (aino->amigaos_mode & A_FIBF_DELETE) != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DELETE_PROTECTED); + return; + } + if (create != 2) { + if ((((mode & aino->amigaos_mode) & A_FIBF_WRITE) != 0 || unit->ui.readonly) + && fallback) + { + mode &= ~A_FIBF_WRITE; + } + /* Kick 1.3 doesn't check read and write access bits - maybe it would be + * simpler just not to do that either. */ + if ((mode & A_FIBF_WRITE) != 0 && unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); + return; + } + if (((mode & aino->amigaos_mode) & A_FIBF_WRITE) != 0 + || mode == 0) + { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_WRITE_PROTECTED); + return; + } + if (((mode & aino->amigaos_mode) & A_FIBF_READ) != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_READ_PROTECTED); + return; + } + } + } else if (create == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } else { + /* Object does not exist. aino points to containing directory. */ + aino = create_child_aino (unit, aino, my_strdup (bstr_cut (unit, name)), 0); + if (aino == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_IS_FULL); /* best we can do */ + return; + } + aino_created = 1; + } - TRACE(("ACTION_EXAMINE_NEXT(0x%lx,0x%lx)\n", lock, info)); - DUMPLOCK(lock); + prepare_for_open (aino->nname); + + openmode = (((mode & A_FIBF_READ) == 0 ? O_WRONLY + : (mode & A_FIBF_WRITE) == 0 ? O_RDONLY + : O_RDWR) + | (create ? O_CREAT : 0) + | (create == 2 ? O_TRUNC : 0)); - do_examine(unit, packet, &examine_keys[get_long(info)], info); + fd = open (aino->nname, openmode | O_BINARY, 0777); + + if (fd < 0) { + if (aino_created) + delete_aino (unit, aino); + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno ()); + return; + } + k = new_key (unit); + k->fd = fd; + k->aino = aino; + + put_long (fh+36, k->uniq); + if (create == 2) + aino->elock = 1; + else + aino->shlock++; + de_recycle_aino (unit, aino); + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -do_find(Unit* unit, DosPacket* packet, mode_t mode) +action_fh_from_lock (Unit *unit, dpacket packet) { - CPTR fh = packet->arg1 << 2; - CPTR lock = packet->arg2 << 2; - CPTR name = packet->arg3 << 2; + uaecptr fh = GET_PCK_ARG1 (packet) << 2; + uaecptr lock = GET_PCK_ARG2 (packet) << 2; + a_inode *aino; Key *k; - struct stat st; + int fd; + mode_t openmode; + int mode; - TRACE(("ACTION_FIND_*(0x%lx,0x%lx,\"%s\",%d)\n",fh,lock,bstr(name),mode)); - DUMPLOCK(lock); + TRACE(("ACTION_FH_FROM_LOCK(0x%lx,0x%lx)\n",fh,lock)); + DUMPLOCK(unit,lock); - k = make_key(unit, lock, bstr(name)); - if(!k) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + if (!lock) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, 0); return; } - /* Fixme: may not quite be right */ - if (0 == stat (k->path, &st)) { - if (S_ISDIR (st.st_mode)) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_OBJECT_WRONG_TYPE; - return; - } - } + aino = lookup_aino (unit, get_long (lock + 4)); + if (aino == 0) + aino = &unit->rootnode; + mode = aino->amigaos_mode; /* Use same mode for opened filehandle as existing Lock() */ - k->fd = open(k->path, mode | O_BINARY, 0777); + prepare_for_open (aino->nname); - if(k->fd < 0) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); + openmode = (((mode & A_FIBF_READ) == 0 ? O_WRONLY + : (mode & A_FIBF_WRITE) == 0 ? O_RDONLY + : O_RDWR)); + + fd = open (aino->nname, openmode | O_BINARY, 0777); + + if (fd < 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno()); return; } - success: - put_long(fh+36, k->uniq); + k = new_key (unit); + k->fd = fd; + k->aino = aino; + + put_long (fh+36, k->uniq); + /* I don't think I need to play with shlock count here, because I'm + opening from an existing lock ??? */ - packet->res1 = DOS_TRUE; + /* Is this right? I don't completely understand how this works. Do I + also need to free_lock() my lock, since nobody else is going to? */ + de_recycle_aino (unit, aino); + PUT_PCK_RES1 (packet, DOS_TRUE); + /* PUT_PCK_RES2 (packet, k->uniq); - this shouldn't be necessary, try without it */ } static void -action_find_input(Unit* unit, DosPacket* packet) +action_find_input (Unit *unit, dpacket packet) { - do_find(unit, packet, O_RDWR); + do_find(unit, packet, A_FIBF_READ|A_FIBF_WRITE, 0, 1); } static void -action_find_output(Unit* unit, DosPacket* packet) +action_find_output (Unit *unit, dpacket packet) { - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } - do_find(unit, packet, O_RDWR|O_CREAT|O_TRUNC); + do_find(unit, packet, A_FIBF_READ|A_FIBF_WRITE, 2, 0); } static void -action_find_write(Unit* unit, DosPacket* packet) +action_find_write (Unit *unit, dpacket packet) { - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } - do_find(unit, packet, O_RDWR); + do_find(unit, packet, A_FIBF_READ|A_FIBF_WRITE, 1, 0); } static void -action_end(Unit* unit, DosPacket* packet) +action_end (Unit *unit, dpacket packet) { Key *k; - TRACE(("ACTION_END(0x%lx)\n", packet->arg1)); + TRACE(("ACTION_END(0x%lx)\n", GET_PCK_ARG1 (packet))); - k = lookup_key(packet->arg1); - free_key(k); - packet->res1 = DOS_TRUE; + k = lookup_key (unit, GET_PCK_ARG1 (packet)); + if (k != 0) { + if (k->aino->elock) + k->aino->elock = 0; + else + k->aino->shlock--; + recycle_aino (unit, k->aino); + free_key (unit, k); + } + PUT_PCK_RES1 (packet, DOS_TRUE); + PUT_PCK_RES2 (packet, 0); } static void -action_read(Unit* unit, DosPacket* packet) +action_read (Unit *unit, dpacket packet) { - Key *k = lookup_key(packet->arg1); - CPTR addr = packet->arg2; - long size = (LONG)packet->arg3; + Key *k = lookup_key (unit, GET_PCK_ARG1 (packet)); + uaecptr addr = GET_PCK_ARG2 (packet); + long size = (uae_s32)GET_PCK_ARG3 (packet); int actual; - TRACE(("ACTION_READ(%s,0x%lx,%ld)\n",k->path,addr,size)); + if (k == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + /* PUT_PCK_RES2 (packet, EINVAL); */ + return; + } + TRACE(("ACTION_READ(%s,0x%lx,%ld)\n",k->aino->nname,addr,size)); #ifdef RELY_ON_LOADSEG_DETECTION - /* HACK HACK HACK HACK + /* HACK HACK HACK HACK * Try to detect a LoadSeg() */ if (k->file_pos == 0 && size >= 4) { unsigned char buf[4]; @@ -1025,789 +1960,1260 @@ action_read(Unit* unit, DosPacket* packe } #endif if (valid_address (addr, size)) { - UBYTE *realpt; + uae_u8 *realpt; realpt = get_real_address (addr); actual = read(k->fd, realpt, size); - if (actual <= 0) { - packet->res1 = 0; - packet->res2 = dos_errno(); + if (actual == 0) { + PUT_PCK_RES1 (packet, 0); + PUT_PCK_RES2 (packet, 0); + } else if (actual < 0) { + PUT_PCK_RES1 (packet, 0); + PUT_PCK_RES2 (packet, dos_errno()); } else { - packet->res1 = actual; + PUT_PCK_RES1 (packet, actual); k->file_pos += actual; } } else { char *buf; - fprintf (stderr, "unixfs warning: Bad pointer passed for read\n"); + write_log ("unixfs warning: Bad pointer passed for read: %08x\n", addr); /* ugh this is inefficient but easy */ buf = (char *)malloc(size); - if(!buf) { - packet->res1 = -1; - packet->res2 = ERROR_NO_FREE_STORE; + if (!buf) { + PUT_PCK_RES1 (packet, -1); + PUT_PCK_RES2 (packet, ERROR_NO_FREE_STORE); return; } actual = read(k->fd, buf, size); - if (actual <= 0) { - packet->res1 = 0; - packet->res2 = dos_errno(); + if (actual < 0) { + PUT_PCK_RES1 (packet, 0); + PUT_PCK_RES2 (packet, dos_errno()); } else { int i; - packet->res1 = actual; - for(i = 0; i < actual; i++) + PUT_PCK_RES1 (packet, actual); + for (i = 0; i < actual; i++) put_byte(addr + i, buf[i]); k->file_pos += actual; } - free(buf); + free (buf); } } static void -action_write(Unit* unit, DosPacket* packet) +action_write (Unit *unit, dpacket packet) { - Key*k = lookup_key(packet->arg1); - CPTR addr = packet->arg2; - long size = packet->arg3; + Key *k = lookup_key (unit, GET_PCK_ARG1 (packet)); + uaecptr addr = GET_PCK_ARG2 (packet); + long size = GET_PCK_ARG3 (packet); char *buf; int i; - TRACE(("ACTION_WRITE(%s,0x%lx,%ld)\n",k->path,addr,size)); + if (k == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + /* PUT_PCK_RES2 (packet, EINVAL); */ + return; + } + + TRACE(("ACTION_WRITE(%s,0x%lx,%ld)\n",k->aino->nname,addr,size)); - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } /* ugh this is inefficient but easy */ buf = (char *)malloc(size); - if(!buf) { - packet->res1 = -1; - packet->res2 = ERROR_NO_FREE_STORE; + if (!buf) { + PUT_PCK_RES1 (packet, -1); + PUT_PCK_RES2 (packet, ERROR_NO_FREE_STORE); return; } - for(i = 0; i < size; i++) + for (i = 0; i < size; i++) buf[i] = get_byte(addr + i); - packet->res1 = write(k->fd, buf, size); - if(packet->res1 != size) - packet->res2 = dos_errno(); - if (packet->res1 >= 0) - k->file_pos += packet->res1; + PUT_PCK_RES1 (packet, write(k->fd, buf, size)); + if (GET_PCK_RES1 (packet) != size) + PUT_PCK_RES2 (packet, dos_errno ()); + if (GET_PCK_RES1 (packet) >= 0) + k->file_pos += GET_PCK_RES1 (packet); - free(buf); + free (buf); } static void -action_seek(Unit* unit, DosPacket* packet) +action_seek (Unit *unit, dpacket packet) { - Key* k = lookup_key(packet->arg1); - long pos = (LONG)packet->arg2; - long mode = (LONG)packet->arg3; + Key *k = lookup_key (unit, GET_PCK_ARG1 (packet)); + long pos = (uae_s32)GET_PCK_ARG2 (packet); + long mode = (uae_s32)GET_PCK_ARG3 (packet); off_t res; long old; int whence = SEEK_CUR; - if(mode > 0) whence = SEEK_END; - if(mode < 0) whence = SEEK_SET; - TRACE(("ACTION_SEEK(%s,%d,%d)\n",k->path,pos,mode)); + if (k == 0) { + PUT_PCK_RES1 (packet, -1); + PUT_PCK_RES2 (packet, ERROR_INVALID_LOCK); + return; + } - old = lseek(k->fd, 0, SEEK_CUR); - res = lseek(k->fd, pos, whence); + if (mode > 0) whence = SEEK_END; + if (mode < 0) whence = SEEK_SET; - if(-1 == res) - packet->res1 = res; - else - packet->res1 = old; + TRACE(("ACTION_SEEK(%s,%d,%d)\n", k->aino->nname, pos, mode)); + + old = lseek (k->fd, 0, SEEK_CUR); + res = lseek (k->fd, pos, whence); + + if (-1 == res) { + PUT_PCK_RES1 (packet, res); + PUT_PCK_RES2 (packet, ERROR_SEEK_ERROR); + } else + PUT_PCK_RES1 (packet, old); k->file_pos = res; } static void -action_set_protect(Unit* unit, DosPacket* packet) +action_set_protect (Unit *unit, dpacket packet) { - CPTR lock = packet->arg2 << 2; - CPTR name = packet->arg3 << 2; - ULONG mask = packet->arg4; - struct stat statbuf; - mode_t mode; - Key *k; + uaecptr lock = GET_PCK_ARG2 (packet) << 2; + uaecptr name = GET_PCK_ARG3 (packet) << 2; + uae_u32 mask = GET_PCK_ARG4 (packet); + a_inode *a; + uae_u32 err; - TRACE(("ACTION_SET_PROTECT(0x%lx,\"%s\",0x%lx)\n",lock,bstr(name),mask)); + TRACE(("ACTION_SET_PROTECT(0x%lx,\"%s\",0x%lx)\n", lock, bstr (unit, name), mask)); - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } - k = make_key(unit, lock, bstr(name)); - if(!k) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + a = find_aino (unit, lock, bstr (unit, name), &err); + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); return; } - if(-1 == stat(k->path, &statbuf)) { - free_key(k); - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_OBJECT_NOT_FOUND; - return; + err = fsdb_set_file_attrs (a, mask); + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + } else { + PUT_PCK_RES1 (packet, DOS_TRUE); } +} - mode = statbuf.st_mode; +static void action_set_comment (Unit * unit, dpacket packet) +{ + uaecptr lock = GET_PCK_ARG2 (packet) << 2; + uaecptr name = GET_PCK_ARG3 (packet) << 2; + uaecptr comment = GET_PCK_ARG4 (packet) << 2; + char *commented; + a_inode *a; + uae_u32 err; + long res1, res2; - if(mask & (1 << 3)) - mode &= ~S_IRUSR; - else - mode |= S_IRUSR; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); + return; + } - if(mask & (1 << 2) || mask & (1 << 0)) - mode &= ~S_IWUSR; - else - mode |= S_IWUSR; + commented = bstr (unit, comment); + commented = strlen (commented) > 0 ? my_strdup (commented) : 0; + TRACE (("ACTION_SET_COMMENT(0x%lx,\"%s\")\n", lock, commented)); - if(mask & (1 << 1)) - mode &= ~S_IXUSR; - else - mode |= S_IXUSR; + a = find_aino (unit, lock, bstr (unit, name), &err); + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); - if(-1 == chmod(k->path, mode)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - } else { - packet->res1 = DOS_TRUE; + maybe_free_and_out: + if (commented) + free (commented); + return; } - free_key(k); + PUT_PCK_RES1 (packet, DOS_TRUE); + PUT_PCK_RES2 (packet, 0); + if (a->comment == 0 && commented == 0) + goto maybe_free_and_out; + if (a->comment != 0 && commented != 0 && strcmp (a->comment, commented) == 0) + goto maybe_free_and_out; + if (a->comment) + free (a->comment); + a->comment = commented; + a->dirty = 1; } static void -action_same_lock(Unit* unit, DosPacket* packet) +action_same_lock (Unit *unit, dpacket packet) { - CPTR lock1 = packet->arg1 << 2; - CPTR lock2 = packet->arg2 << 2; + uaecptr lock1 = GET_PCK_ARG1 (packet) << 2; + uaecptr lock2 = GET_PCK_ARG2 (packet) << 2; TRACE(("ACTION_SAME_LOCK(0x%lx,0x%lx)\n",lock1,lock2)); - DUMPLOCK(lock1); DUMPLOCK(lock2); + DUMPLOCK(unit, lock1); DUMPLOCK(unit, lock2); - if(!lock1 || !lock2) { - packet->res1 = (lock1 == lock2) ? DOS_TRUE : DOS_FALSE; + if (!lock1 || !lock2) { + PUT_PCK_RES1 (packet, lock1 == lock2 ? DOS_TRUE : DOS_FALSE); } else { - Key* key1 = lookup_key(get_long(lock1 + 4)); - Key* key2 = lookup_key(get_long(lock2 + 4)); - packet->res1 = (0 == strcmp(key1->path, key2->path)) ? DOS_TRUE : DOS_FALSE; + PUT_PCK_RES1 (packet, get_long (lock1 + 4) == get_long (lock2 + 4) ? DOS_TRUE : DOS_FALSE); } } static void -action_parent(Unit* unit, DosPacket* packet) +action_change_mode (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - Key*k; +#define CHANGE_LOCK 0 +#define CHANGE_FH 1 + /* will be CHANGE_FH or CHANGE_LOCK value */ + long type = GET_PCK_ARG1 (packet); + /* either a file-handle or lock */ + uaecptr object = GET_PCK_ARG2 (packet) << 2; + /* will be EXCLUSIVE_LOCK/SHARED_LOCK if CHANGE_LOCK, + * or MODE_OLDFILE/MODE_NEWFILE/MODE_READWRITE if CHANGE_FH */ + long mode = GET_PCK_ARG3 (packet); + uaecptr fh; + a_inode *a = NULL, *olda = NULL; + uae_u32 err = 0; + TRACE(("ACTION_CHANGE_MODE(0x%lx,%d,%d)\n",object,type,mode)); + + if (! object + || (type != CHANGE_FH && type != CHANGE_LOCK)) + { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_INVALID_LOCK); + return; + } - TRACE(("ACTION_PARENT(0x%lx)\n",lock)); + /* @@@ Brian: shouldn't this be good enough to support + CHANGE_FH? */ + if (type == CHANGE_FH) + mode = (mode == 1006 ? -1 : -2); + fh = (type == CHANGE_LOCK ? get_long (object + 4) : object); + a = lookup_aino (unit, get_long (object + 4)); + if (! a) + err = ERROR_INVALID_LOCK; + else { + if (mode == -1) { + if (a->shlock > 1) + err = ERROR_OBJECT_IN_USE; + else { + a->shlock = 0; + a->elock = 1; + } + } else { /* Must be SHARED_LOCK == -2 */ + a->elock = 0; + a->shlock++; + } + } - if(!lock) { - packet->res1 = 0; - packet->res2 = 0; + if (err) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); return; + } else { + de_recycle_aino (unit, a); + PUT_PCK_RES1 (packet, DOS_TRUE); } +} - k = dup_key(lookup_key(get_long(lock + 4))); - if(0 == strcmp(k->path, unit->ui.rootdir)) { - free_key(k); - packet->res1 = 0; - packet->res2 = 0; +static void +action_parent_common (Unit *unit, dpacket packet, uaecptr fh) +{ + a_inode *olda = lookup_aino (unit, fh); + if (olda == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_INVALID_LOCK); return; } - { - char *x = strrchr(k->path,'/'); - if(!x) { /* ??? This really shouldn't happen! */ - free_key(k); - packet->res1 = 0; - packet->res2 = 0; - return; - } else { - *x = '\0'; - } + + if (olda->parent == 0) { + PUT_PCK_RES1 (packet, 0); + PUT_PCK_RES2 (packet, 0); + return; + } + if (olda->parent->elock) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_IN_USE); + return; } - packet->res1 = make_lock(unit, k, -2) >> 2; + olda->parent->shlock++; + de_recycle_aino (unit, olda->parent); + PUT_PCK_RES1 (packet, make_lock (unit, olda->parent->uniq, -2) >> 2); } static void -action_create_dir(Unit* unit, DosPacket* packet) +action_parent_fh (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - CPTR name = packet->arg2 << 2; - Key* k; + uaecptr fh = GET_PCK_ARG1 (packet) << 2; + TRACE(("ACTION_PARENT_FH(0x%lx)\n",fh)); - TRACE(("ACTION_CREATE_DIR(0x%lx,\"%s\")\n",lock,bstr(name))); + if (! fh) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_FOUND); + return; + } + action_parent_common (unit, packet, fh); +} + +static void +action_parent (Unit *unit, dpacket packet) +{ + uaecptr lock = GET_PCK_ARG1 (packet) << 2; - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + TRACE(("ACTION_PARENT(0x%lx)\n",lock)); + + if (!lock) { + PUT_PCK_RES1 (packet, 0); + PUT_PCK_RES2 (packet, 0); return; } + action_parent_common (unit, packet, get_long (lock + 4)); +} - k = make_key(unit, lock, bstr(name)); +static void +action_create_dir (Unit *unit, dpacket packet) +{ + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + uaecptr name = GET_PCK_ARG2 (packet) << 2; + a_inode *aino; + uae_u32 err; - if(!k) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + TRACE(("ACTION_CREATE_DIR(0x%lx,\"%s\")\n", lock, bstr (unit, name))); + + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } -#ifndef AMIGA - if(-1 == mkdir(k->path, 0777)) -#else - if(-1 == mkdir(k->path)) -#endif - { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); + + aino = find_aino (unit, lock, bstr (unit, name), &err); + if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_FOUND)) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } + if (err == 0) { + /* Object exists. */ + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_EXISTS); + return; + } + /* Object does not exist. aino points to containing directory. */ + aino = create_child_aino (unit, aino, my_strdup (bstr_cut (unit, name)), 1); + if (aino == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_IS_FULL); /* best we can do */ + return; + } + + if (mkdir (aino->nname, 0777) == -1) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno()); return; } - packet->res1 = make_lock(unit, k, -2) >> 2; + aino->shlock = 1; + de_recycle_aino (unit, aino); + PUT_PCK_RES1 (packet, make_lock (unit, aino->uniq, -2) >> 2); } static void -action_delete_object(Unit* unit, DosPacket* packet) +action_examine_fh (Unit *unit, dpacket packet) { - CPTR lock = packet->arg1 << 2; - CPTR name = packet->arg2 << 2; - Key* k; - struct stat statbuf; + Key *k; + a_inode *aino = 0; + uaecptr info = GET_PCK_ARG2 (packet) << 2; - TRACE(("ACTION_DELETE_OBJECT(0x%lx,\"%s\")\n",lock,bstr(name))); + TRACE(("ACTION_EXAMINE_FH(0x%lx,0x%lx)\n", + GET_PCK_ARG1 (packet), GET_PCK_ARG2 (packet) )); + + k = lookup_key (unit, GET_PCK_ARG1 (packet)); + if (k != 0) + aino = k->aino; + if (aino == 0) + aino = &unit->rootnode; + + get_fileinfo (unit, packet, info, aino); + if (aino->dir) + put_long (info, 0xFFFFFFFF); + else + put_long (info, 0); +} + +/* For a nice example of just how contradictory documentation can be, see the + * Autodoc for DOS:SetFileSize and the Packets.txt description of this packet... + * This implementation tries to mimic the behaviour of the Kick 3.1 ramdisk + * (which seems to match the Autodoc description). */ +static void +action_set_file_size (Unit *unit, dpacket packet) +{ + Key *k, *k1; + off_t offset = GET_PCK_ARG2 (packet); + long mode = (uae_s32)GET_PCK_ARG3 (packet); + int whence = SEEK_CUR; + + if (mode > 0) whence = SEEK_END; + if (mode < 0) whence = SEEK_SET; - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + TRACE(("ACTION_SET_FILE_SIZE(0x%lx, %d, 0x%x)\n", GET_PCK_ARG1 (packet), offset, mode)); + + k = lookup_key (unit, GET_PCK_ARG1 (packet)); + if (k == 0) { + PUT_PCK_RES1 (packet, DOS_TRUE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_FOUND); return; } - k = make_key(unit, lock, bstr(name)); + /* If any open files have file pointers beyond this size, truncate only + * so far that these pointers do not become invalid. */ + for (k1 = unit->keys; k1; k1 = k1->next) { + if (k != k1 && k->aino == k1->aino) { + if (k1->file_pos > offset) + offset = k1->file_pos; + } + } + + /* Write one then truncate: that should give the right size in all cases. */ + offset = lseek (k->fd, offset, whence); + write (k->fd, /* whatever */(char *)&k1, 1); + if (k->file_pos > offset) + k->file_pos = offset; + lseek (k->fd, k->file_pos, SEEK_SET); - if(!k) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + /* Brian: no bug here; the file _must_ be one byte too large after writing + The write is supposed to guarantee that the file can't be smaller than + the requested size, the truncate guarantees that it can't be larger. + If we were to write one byte earlier we'd clobber file data. */ + if (truncate (k->aino->nname, offset) == -1) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno ()); return; } - if(-1 == stat(k->path, &statbuf)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); + + PUT_PCK_RES1 (packet, offset); + PUT_PCK_RES2 (packet, 0); +} + +static void +action_delete_object (Unit *unit, dpacket packet) +{ + uaecptr lock = GET_PCK_ARG1 (packet) << 2; + uaecptr name = GET_PCK_ARG2 (packet) << 2; + a_inode *a; + uae_u32 err; + + TRACE(("ACTION_DELETE_OBJECT(0x%lx,\"%s\")\n", lock, bstr (unit, name))); + + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } - if(S_ISDIR(statbuf.st_mode)) { - if(-1 == rmdir(k->path)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); + + a = find_aino (unit, lock, bstr (unit, name), &err); + + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; + } + if (a->amigaos_mode & A_FIBF_DELETE) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DELETE_PROTECTED); + return; + } + if (a->shlock > 0 || a->elock) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_IN_USE); + return; + } + if (a->dir) { + if (rmdir (a->nname) == -1) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno()); return; } } else { - if(-1 == unlink(k->path)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); + if (unlink (a->nname) == -1) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno()); return; } } - free_key(k); - packet->res1 = DOS_TRUE; + if (a->child != 0) { + write_log ("Serious error in action_delete_object.\n"); + } else { + delete_aino (unit, a); + } + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_set_date(Unit* unit, DosPacket* packet) +action_set_date (Unit *unit, dpacket packet) { - CPTR lock = packet->arg2 << 2; - CPTR name = packet->arg3 << 2; - CPTR date = packet->arg4; - Key* k; -#if !defined(AMIGA) + uaecptr lock = GET_PCK_ARG2 (packet) << 2; + uaecptr name = GET_PCK_ARG3 (packet) << 2; + uaecptr date = GET_PCK_ARG4 (packet); + a_inode *a; struct utimbuf ut; -#endif + uae_u32 err; - TRACE(("ACTION_SET_DATE(0x%lx,\"%s\")\n",lock,bstr(name))); + TRACE(("ACTION_SET_DATE(0x%lx,\"%s\")\n", lock, bstr (unit, name))); - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } -#if !defined(AMIGA) - ut.actime = ut.modtime = put_time(get_long(date),get_long(date+4),get_long(date+8)); -#endif - k = make_key(unit, lock, bstr(name)); - - if(!k) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; - return; - } -#if !defined(AMIGA) - if(-1 == utime(k->path, &ut)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k); - return; - } -#endif - free_key(k); - packet->res1 = DOS_TRUE; + ut.actime = ut.modtime = put_time(get_long (date), get_long (date + 4), + get_long (date + 8)); + a = find_aino (unit, lock, bstr (unit, name), &err); + if (err == 0 && utime (a->nname, &ut) == -1) + err = dos_errno (); + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + } else + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_rename_object(Unit* unit, DosPacket* packet) +action_rename_object (Unit *unit, dpacket packet) { - CPTR lock1 = packet->arg1 << 2; - CPTR name1 = packet->arg2 << 2; - Key* k1; - CPTR lock2 = packet->arg3 << 2; - CPTR name2 = packet->arg4 << 2; - Key* k2; + uaecptr lock1 = GET_PCK_ARG1 (packet) << 2; + uaecptr name1 = GET_PCK_ARG2 (packet) << 2; + uaecptr lock2 = GET_PCK_ARG3 (packet) << 2; + uaecptr name2 = GET_PCK_ARG4 (packet) << 2; + a_inode *a1, *a2; + uae_u32 err1, err2; - TRACE(("ACTION_RENAME_OBJECT(0x%lx,\"%s\",",lock1,bstr(name1))); - TRACE(("0x%lx,\"%s\")\n",lock2,bstr(name2))); + TRACE(("ACTION_RENAME_OBJECT(0x%lx,\"%s\",", lock1, bstr (unit, name1))); + TRACE(("0x%lx,\"%s\")\n", lock2, bstr (unit, name2))); - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } - k1 = make_key(unit, lock1, bstr(name1)); - if(!k1) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + a1 = find_aino (unit, lock1, bstr (unit, name1), &err1); + if (err1 != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err1); + return; + } + /* See whether the other name already exists in the filesystem. */ + a2 = find_aino (unit, lock2, bstr (unit, name2), &err2); + if (a2 == a1) { + /* Renaming to the same name, but possibly different case. */ + if (strcmp (a1->aname, bstr_cut (unit, name2)) == 0) { + /* Exact match -> do nothing. */ + PUT_PCK_RES1 (packet, DOS_TRUE); + return; + } + a2 = a2->parent; + } else if (a2 == 0 || err2 != ERROR_OBJECT_NOT_FOUND) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err2 == 0 ? ERROR_OBJECT_EXISTS : err2); return; } - k2 = make_key(unit, lock2, bstr(name2)); - if(!k2) { - free_key(k1); - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_NO_FREE_STORE; + + a2 = create_child_aino (unit, a2, bstr_cut (unit, name2), a1->dir); + if (a2 == 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_IS_FULL); /* best we can do */ return; } - if(-1 == rename(k1->path, k2->path)) { - packet->res1 = DOS_FALSE; - packet->res2 = dos_errno(); - free_key(k1); - free_key(k2); + /* @@@ what should we do if there are locks on a1? */ + if (-1 == rename (a1->nname, a2->nname)) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, dos_errno ()); return; } - free_key(k1); - free_key(k2); - packet->res1 = DOS_TRUE; + a2->comment = a1->comment; + a1->comment = 0; + a2->amigaos_mode = a1->amigaos_mode; + move_aino_children (unit, a1, a2); + delete_aino (unit, a1); + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_current_volume(Unit* unit, DosPacket* packet) +action_current_volume (Unit *unit, dpacket packet) { - packet->res1 = unit->volume >> 2; + PUT_PCK_RES1 (packet, unit->volume >> 2); } static void -action_rename_disk(Unit* unit, DosPacket* packet) +action_rename_disk (Unit *unit, dpacket packet) { - CPTR name = packet->arg1 << 2; + uaecptr name = GET_PCK_ARG1 (packet) << 2; int i; int namelen; - TRACE(("ACTION_RENAME_DISK(\"%s\")\n", bstr(name))); + TRACE(("ACTION_RENAME_DISK(\"%s\")\n", bstr (unit, name))); - if(unit->ui.readonly) { - packet->res1 = DOS_FALSE; - packet->res2 = ERROR_DISK_WRITE_PROTECTED; + if (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); return; } /* get volume name */ - namelen = get_byte(name++); - free(unit->ui.volname); - unit->ui.volname = (char *) malloc(namelen + 1); - for(i = 0; i < namelen; i++) - unit->ui.volname[i] = get_byte(name++); + namelen = get_byte (name); name++; + free (unit->ui.volname); + unit->ui.volname = (char *) xmalloc (namelen + 1); + for (i = 0; i < namelen; i++, name++) + unit->ui.volname[i] = get_byte (name); unit->ui.volname[i] = 0; - put_byte(unit->volume + 44, namelen); - for(i = 0; i < namelen; i++) - put_byte(unit->volume + 45 + i, unit->ui.volname[i]); + put_byte (unit->volume + 44, namelen); + for (i = 0; i < namelen; i++) + put_byte (unit->volume + 45 + i, unit->ui.volname[i]); - packet->res1 = DOS_TRUE; + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_is_filesystem(Unit* unit, DosPacket* packet) +action_is_filesystem (Unit *unit, dpacket packet) { - packet->res1 = DOS_TRUE; + PUT_PCK_RES1 (packet, DOS_TRUE); } static void -action_flush(Unit* unit, DosPacket* packet) +action_flush (Unit *unit, dpacket packet) { /* sync(); */ /* pretty drastic, eh */ - packet->res1 = DOS_TRUE; + PUT_PCK_RES1 (packet, DOS_TRUE); } -static ULONG -filesys_handler(void) -{ - DosPacket packet; - Unit *unit = find_unit(regs.a[5]); - - /* got DosPacket in A4 */ - packet.addr = regs.a[4]; - packet.type = get_long(packet.addr + dp_Type); - packet.res1 = get_long(packet.addr + dp_Res1); - packet.res2 = get_long(packet.addr + dp_Res2); - packet.arg1 = get_long(packet.addr + dp_Arg1); - packet.arg2 = get_long(packet.addr + dp_Arg2); - packet.arg3 = get_long(packet.addr + dp_Arg3); - packet.arg4 = get_long(packet.addr + dp_Arg4); - - if(!unit) { - startup(&packet); - put_long(packet.addr + dp_Res1, packet.res1); - put_long(packet.addr + dp_Res2, packet.res2); - return 0; - } - - if(!unit->volume) { - printf("no volume\n"); +/* We don't want multiple interrupts to be active at the same time. I don't + * know whether AmigaOS takes care of that, but this does. */ +static uae_sem_t singlethread_int_sem; + +static uae_u32 exter_int_helper (void) +{ + UnitInfo *uip = current_mountinfo->ui; + uaecptr port; + static int unit_no; + + switch (m68k_dreg (regs, 0)) { + case 0: + /* Determine whether a given EXTER interrupt is for us. */ + if (uae_int_requested) { + if (uae_sem_trywait (&singlethread_int_sem) != 0) + /* Pretend it isn't for us. We might get it again later. */ + return 0; + /* Clear the interrupt flag _before_ we do any processing. + * That way, we can get too many interrupts, but never not + * enough. */ + uae_int_requested = 0; + unit_no = 0; + return 1; + } return 0; - } - - switch(packet.type) { - case ACTION_LOCATE_OBJECT: - action_lock(unit, &packet); - break; - - case ACTION_FREE_LOCK: - action_free_lock(unit, &packet); - break; - - case ACTION_COPY_DIR: - action_dup_lock(unit, &packet); - break; - - case ACTION_DISK_INFO: - action_disk_info(unit, &packet); - break; - - case ACTION_INFO: - action_info(unit, &packet); - break; - - case ACTION_EXAMINE_OBJECT: - action_examine_object(unit, &packet); - break; - - case ACTION_EXAMINE_NEXT: - action_examine_next(unit, &packet); - break; - - case ACTION_FIND_INPUT: - action_find_input(unit, &packet); - break; - - case ACTION_FIND_WRITE: - action_find_write(unit, &packet); - break; - - case ACTION_FIND_OUTPUT: - action_find_output(unit, &packet); - break; - - case ACTION_END: - action_end(unit, &packet); - break; - - case ACTION_READ: - action_read(unit, &packet); - break; - - case ACTION_WRITE: - action_write(unit, &packet); - break; - - case ACTION_SEEK: - action_seek(unit, &packet); - break; - - case ACTION_SET_PROTECT: - action_set_protect(unit, &packet); + case 1: + /* Release a message_lock. This is called as soon as the message is + * received by the assembly code. We use the opportunity to check + * whether we have some locks that we can give back to the assembler + * code. + * Note that this is called from the main loop, unlike the other cases + * in this switch statement which are called from the interrupt handler. + */ +#ifdef UAE_FILESYS_THREADS + { + Unit *unit = find_unit (m68k_areg (regs, 5)); + unit->cmds_complete = unit->cmds_acked; + while (comm_pipe_has_data (unit->ui.back_pipe)) { + uaecptr locks, lockend; + locks = read_comm_pipe_int_blocking (unit->ui.back_pipe); + lockend = locks; + while (get_long (lockend) != 0) + lockend = get_long (lockend); + put_long (lockend, get_long (m68k_areg (regs, 3))); + put_long (m68k_areg (regs, 3), locks); + } + } +#else + write_log ("exter_int_helper should not be called with arg 1!\n"); +#endif break; + case 2: + /* Find work that needs to be done: + * return d0 = 0: none + * d0 = 1: PutMsg(), port in a0, message in a1 + * d0 = 2: Signal(), task in a1, signal set in d1 + * d0 = 3: ReplyMsg(), message in a1 */ + +#ifdef SUPPORT_THREADS + /* First, check signals/messages */ + while (comm_pipe_has_data (&native2amiga_pending)) { + switch (read_comm_pipe_int_blocking (&native2amiga_pending)) { + case 0: /* Signal() */ + m68k_areg (regs, 1) = read_comm_pipe_u32_blocking (&native2amiga_pending); + m68k_dreg (regs, 1) = read_comm_pipe_u32_blocking (&native2amiga_pending); + return 2; + + case 1: /* PutMsg() */ + m68k_areg (regs, 0) = read_comm_pipe_u32_blocking (&native2amiga_pending); + m68k_areg (regs, 1) = read_comm_pipe_u32_blocking (&native2amiga_pending); + return 1; + + case 2: /* ReplyMsg() */ + m68k_areg (regs, 1) = read_comm_pipe_u32_blocking (&native2amiga_pending); + return 3; - case ACTION_SAME_LOCK: - action_same_lock(unit, &packet); - break; + default: + write_log ("exter_int_helper: unknown native action\n"); + break; + } + } +#endif - case ACTION_PARENT: - action_parent(unit, &packet); + /* Find some unit that needs a message sent, and return its port, + * or zero if all are done. + * Take care not to dereference self for units that didn't have their + * startup packet sent. */ + for (;;) { + if (unit_no >= current_mountinfo->num_units) + return 0; + + if (uip[unit_no].self != 0 + && uip[unit_no].self->cmds_acked == uip[unit_no].self->cmds_complete + && uip[unit_no].self->cmds_acked != uip[unit_no].self->cmds_sent) + break; + unit_no++; + } + uip[unit_no].self->cmds_acked = uip[unit_no].self->cmds_sent; + port = uip[unit_no].self->port; + if (port) { + m68k_areg (regs, 0) = port; + m68k_areg (regs, 1) = find_unit (port)->dummy_message; + unit_no++; + return 1; + } break; - - case ACTION_CREATE_DIR: - action_create_dir(unit, &packet); + case 4: + /* Exit the interrupt, and release the single-threading lock. */ + uae_sem_post (&singlethread_int_sem); break; - case ACTION_DELETE_OBJECT: - action_delete_object(unit, &packet); + default: + write_log ("Shouldn't happen in exter_int_helper.\n"); break; + } + return 0; +} - case ACTION_RENAME_OBJECT: - action_rename_object(unit, &packet); - break; +static int handle_packet (Unit *unit, dpacket pck) +{ + uae_s32 type = GET_PCK_TYPE (pck); + PUT_PCK_RES2 (pck, 0); + switch (type) { + case ACTION_LOCATE_OBJECT: action_lock (unit, pck); break; + case ACTION_FREE_LOCK: action_free_lock (unit, pck); break; + case ACTION_COPY_DIR: action_dup_lock (unit, pck); break; + case ACTION_DISK_INFO: action_disk_info (unit, pck); break; + case ACTION_INFO: action_info (unit, pck); break; + case ACTION_EXAMINE_OBJECT: action_examine_object (unit, pck); break; + case ACTION_EXAMINE_NEXT: action_examine_next (unit, pck); break; + case ACTION_FIND_INPUT: action_find_input (unit, pck); break; + case ACTION_FIND_WRITE: action_find_write (unit, pck); break; + case ACTION_FIND_OUTPUT: action_find_output (unit, pck); break; + case ACTION_END: action_end (unit, pck); break; + case ACTION_READ: action_read (unit, pck); break; + case ACTION_WRITE: action_write (unit, pck); break; + case ACTION_SEEK: action_seek (unit, pck); break; + case ACTION_SET_PROTECT: action_set_protect (unit, pck); break; + case ACTION_SET_COMMENT: action_set_comment (unit, pck); break; + case ACTION_SAME_LOCK: action_same_lock (unit, pck); break; + case ACTION_PARENT: action_parent (unit, pck); break; + case ACTION_CREATE_DIR: action_create_dir (unit, pck); break; + case ACTION_DELETE_OBJECT: action_delete_object (unit, pck); break; + case ACTION_RENAME_OBJECT: action_rename_object (unit, pck); break; + case ACTION_SET_DATE: action_set_date (unit, pck); break; + case ACTION_CURRENT_VOLUME: action_current_volume (unit, pck); break; + case ACTION_RENAME_DISK: action_rename_disk (unit, pck); break; + case ACTION_IS_FILESYSTEM: action_is_filesystem (unit, pck); break; + case ACTION_FLUSH: action_flush (unit, pck); break; + + /* 2.0+ packet types */ + case ACTION_SET_FILE_SIZE: action_set_file_size (unit, pck); break; + case ACTION_EXAMINE_FH: action_examine_fh (unit, pck); break; + case ACTION_FH_FROM_LOCK: action_fh_from_lock (unit, pck); break; + case ACTION_CHANGE_MODE: action_change_mode (unit, pck); break; + case ACTION_PARENT_FH: action_parent_fh (unit, pck); break; + + /* unsupported packets */ + case ACTION_LOCK_RECORD: + case ACTION_FREE_RECORD: + case ACTION_COPY_DIR_FH: + case ACTION_EXAMINE_ALL: + case ACTION_MAKE_LINK: + case ACTION_READ_LINK: + case ACTION_FORMAT: + case ACTION_ADD_NOTIFY: + case ACTION_REMOVE_NOTIFY: + default: + TRACE(("*** UNSUPPORTED PACKET %ld\n", type)); + return 0; + } + return 1; +} - case ACTION_SET_DATE: - action_set_date(unit, &packet); - break; +#ifdef UAE_FILESYS_THREADS +static void *filesys_penguin (void *unit_v) +{ + UnitInfo *ui = (UnitInfo *)unit_v; + for (;;) { + uae_u8 *pck; + uae_u8 *msg; + uae_u32 morelocks; + int i; + + pck = (uae_u8 *)read_comm_pipe_pvoid_blocking (ui->unit_pipe); + msg = (uae_u8 *)read_comm_pipe_pvoid_blocking (ui->unit_pipe); + morelocks = (uae_u32)read_comm_pipe_int_blocking (ui->unit_pipe); + + if (ui->reset_state == FS_GO_DOWN) { + if (pck != 0) + continue; + /* Death message received. */ + uae_sem_post (&ui->reset_sync_sem); + /* Die. */ + return 0; + } - case ACTION_CURRENT_VOLUME: - action_current_volume(unit, &packet); - break; + put_long (get_long (morelocks), get_long (ui->self->locklist)); + put_long (ui->self->locklist, morelocks); + if (! handle_packet (ui->self, pck)) { + PUT_PCK_RES1 (pck, DOS_FALSE); + PUT_PCK_RES2 (pck, ERROR_ACTION_NOT_KNOWN); + } + /* Mark the packet as processed for the list scan in the assembly code. */ + do_put_mem_long ((uae_u32 *)(msg + 4), -1); + /* Acquire the message lock, so that we know we can safely send the + * message. */ + ui->self->cmds_sent++; + /* The message is sent by our interrupt handler, so make sure an interrupt + * happens. */ + uae_int_requested = 1; + /* Send back the locks. */ + if (get_long (ui->self->locklist) != 0) + write_comm_pipe_int (ui->back_pipe, (int)(get_long (ui->self->locklist)), 0); + put_long (ui->self->locklist, 0); + } + return 0; +} +#endif - case ACTION_RENAME_DISK: - action_rename_disk(unit, &packet); - break; +/* Talk about spaghetti code... */ +static uae_u32 filesys_handler (void) +{ + Unit *unit = find_unit (m68k_areg (regs, 5)); + uaecptr packet_addr = m68k_dreg (regs, 3); + uaecptr message_addr = m68k_areg (regs, 4); + uae_u8 *pck; + uae_u8 *msg; + if (! valid_address (packet_addr, 36) || ! valid_address (message_addr, 14)) { + write_log ("Bad address passed for packet.\n"); + goto error2; + } + pck = get_real_address (packet_addr); + msg = get_real_address (message_addr); - case ACTION_IS_FILESYSTEM: - action_is_filesystem(unit, &packet); - break; +#if 0 + if (unit->reset_state == FS_GO_DOWN) + /* You might as well queue it, if you live long enough */ + return 1; +#endif - case ACTION_FLUSH: - action_flush(unit, &packet); - break; + do_put_mem_long ((uae_u32 *)(msg + 4), -1); + if (!unit || !unit->volume) { + write_log ("Filesystem was not initialized.\n"); + goto error; + } +#ifdef UAE_FILESYS_THREADS + { + /* Get two more locks and hand them over to the other thread. */ + uae_u32 morelocks; + morelocks = get_long (m68k_areg (regs, 3)); + put_long (m68k_areg (regs, 3), get_long (get_long (morelocks))); + put_long (get_long (morelocks), 0); + + /* The packet wasn't processed yet. */ + do_put_mem_long ((uae_u32 *)(msg + 4), 0); + write_comm_pipe_pvoid (unit->ui.unit_pipe, (void *)pck, 0); + write_comm_pipe_pvoid (unit->ui.unit_pipe, (void *)msg, 0); + write_comm_pipe_int (unit->ui.unit_pipe, (int)morelocks, 1); + /* Don't reply yet. */ + return 1; + } +#endif - default: - TRACE(("*** UNSUPPORTED PACKET %ld\n", packet.type)); - packet.res1 = DOS_FALSE; - packet.res2 = ERROR_ACTION_NOT_KNOWN; - break; + if (! handle_packet (unit, pck)) { + error: + PUT_PCK_RES1 (pck, DOS_FALSE); + PUT_PCK_RES2 (pck, ERROR_ACTION_NOT_KNOWN); } + TRACE(("reply: %8lx, %ld\n", GET_PCK_RES1 (pck), GET_PCK_RES2 (pck))); - put_long(packet.addr + dp_Res1, packet.res1); - put_long(packet.addr + dp_Res2, packet.res2); - TRACE(("reply: %8lx, %ld\n", packet.res1, packet.res2)); + error2: return 0; } -static ULONG filesys_diagentry(void) +void filesys_start_threads (void) { - CPTR resaddr = regs.a[2] + 0x10; - - filesys_configdev = regs.a[3]; + UnitInfo *uip; + int i; + + current_mountinfo = dup_mountinfo (currprefs.mountinfo); + + reset_uaedevices (); - if (ROM_hardfile_resid != 0) { - /* Build a struct Resident. This will set up and initialize - * the uae.device */ - put_word(resaddr + 0x0, 0x4AFC); - put_long(resaddr + 0x2, resaddr); - put_long(resaddr + 0x6, resaddr + 0x1A); /* Continue scan here */ - put_word(resaddr + 0xA, 0x8101); /* RTF_AUTOINIT|RTF_COLDSTART; Version 1 */ - put_word(resaddr + 0xC, 0x0305); /* NT_DEVICE; pri 05 */ - put_long(resaddr + 0xE, ROM_hardfile_resname); - put_long(resaddr + 0x12, ROM_hardfile_resid); - put_long(resaddr + 0x16, ROM_hardfile_init); + uip = current_mountinfo->ui; + for (i = 0; i < current_mountinfo->num_units; i++) { + uip[i].unit_pipe = 0; + uip[i].devno = get_new_device (&uip[i].devname, &uip[i].devname_amiga); + +#ifdef UAE_FILESYS_THREADS + if (! is_hardfile (current_mountinfo, i)) { + uip[i].unit_pipe = (smp_comm_pipe *)xmalloc (sizeof (smp_comm_pipe)); + uip[i].back_pipe = (smp_comm_pipe *)xmalloc (sizeof (smp_comm_pipe)); + init_comm_pipe (uip[i].unit_pipe, 50, 3); + init_comm_pipe (uip[i].back_pipe, 50, 1); + start_penguin (filesys_penguin, (void *)(uip + i), &uip[i].tid); + } +#endif } - resaddr += 0x1A; - - return 1; } -static CPTR build_parmpacket(void) +void filesys_reset (void) { - CPTR tmp1; + Unit *u, *u1; + int i; - regs.d[0] = 88; regs.d[1] = 1; /* MEMF_PUBLIC */ - tmp1 = CallLib (get_long(4), -198); /* AllocMem() */ - if (tmp1 == 0) { - fprintf(stderr, "Not enough memory for filesystem!\n"); - return 0; - } + /* We get called once from customreset at the beginning of the program + * before filesys_start_threads has been called. Survive that. */ + if (current_mountinfo == 0) + return; - put_long (tmp1+12, 0); /* Device flags */ - put_long (tmp1+16, 16); /* Env. size */ - put_long (tmp1+20, 128); /* 512 bytes/block */ - put_long (tmp1+24, 0); /* unused */ - put_long (tmp1+28, 1); /* heads */ - put_long (tmp1+32, 1); /* unused */ - put_long (tmp1+36, 32); /* secs per track */ - put_long (tmp1+40, 1); /* reserved blocks */ - put_long (tmp1+44, 0); /* unused */ - put_long (tmp1+48, 0); /* interleave */ - put_long (tmp1+52, 0); /* lowCyl */ -#ifndef __DOS__ - put_long (tmp1+56, 511); /* upperCyl */ -#else - { - extern int numtracks; - put_long (tmp1+56, numtracks-1); /* upperCyl */ + for (u = units; u; u = u1) { + u1 = u->next; + free (u); } -#endif - put_long (tmp1+60, 0); /* Number of buffers */ - put_long (tmp1+64, 0); /* Buffer mem type */ - put_long (tmp1+68, 0x7FFFFFFF); /* largest transfer */ - put_long (tmp1+72, ~1); /* addMask (?) */ - put_long (tmp1+76, (ULONG)-1); /* bootPri */ -#if 0 - if (have36) - put_long (tmp1+80, 0x444f5301); /* DOS\1 */ - else -#endif - put_long (tmp1+80, 0x444f5300); /* DOS\0 */ + unit_num = 0; + units = 0; - put_long (tmp1+84, 0); /* pad */ - return tmp1; + free_mountinfo (current_mountinfo); + current_mountinfo = 0; } -static void make_dev(CPTR param_packet, int unit_no, int is_hardfile, int boot) +void filesys_prepare_reset (void) { - CPTR devicenode, bootnode; + UnitInfo *uip = current_mountinfo->ui; + Unit *u; + int i; - put_long (param_packet, ui[unit_no].devname_amiga); - put_long (param_packet + 4, is_hardfile ? ROM_hardfile_resname : fsdevname); - put_long (param_packet + 8, ui[unit_no].devno); - - regs.a[0] = param_packet; - devicenode = CallLib (EXPANSION_explibbase, -144); /* MakeDosNode() */ - ui[unit_no].startup = get_long(devicenode + 28); - if (!is_hardfile) { - put_long(devicenode+8, 0x0); /* dn_Task */ - put_long(devicenode+16, 0x0); /* dn_Handler */ - put_long(devicenode+20, 4000); /* dn_StackSize */ - put_long(devicenode+32, filesysseglist >> 2); /* dn_SegList */ - put_long(devicenode+36, (ULONG)-1); /* dn_GlobalVec */ - } else { - /* ??? */ - put_long(devicenode+8, 0x0); /* dn_Task */ - put_long(devicenode+16, 0x0); /* dn_Handler */ - put_long(devicenode+32, 0); /* dn_SegList */ +#ifdef UAE_FILESYS_THREADS + for (i = 0; i < current_mountinfo->num_units; i++) { + if (uip[i].unit_pipe != 0) { + uae_sem_init (&uip[i].reset_sync_sem, 0, 0); + uip[i].reset_state = FS_GO_DOWN; + /* send death message */ + write_comm_pipe_int (uip[i].unit_pipe, 0, 0); + write_comm_pipe_int (uip[i].unit_pipe, 0, 0); + write_comm_pipe_int (uip[i].unit_pipe, 0, 1); + uae_sem_wait (&uip[i].reset_sync_sem); + } } - - if (boot) { - if (EXPANSION_haveV36) { - regs.d[0] = -1; regs.d[1] = 0; - regs.a[0] = devicenode; - regs.a[1] = filesys_configdev; - CallLib(EXPANSION_explibbase, -36); - } else { - /* Construct a BootNode and Enqueue() it into eb_MountList */ - regs.d[0] = 20; - regs.d[1] = 0; - bootnode = CallLib (get_long(4), -198); /* AllocMem() */ - - put_word (bootnode + 14, 0); /* Flags (??) */ - put_long (bootnode + 16, devicenode); - put_word (bootnode + 8, 0x10FF-unit_no); /* Type/Pri */ - put_long (bootnode + 10, filesys_configdev); /* Name */ - put_long (bootnode + 0, 0); - put_long (bootnode + 4, 0); - regs.a[0] = EXPANSION_explibbase + 74; /* MountList */ - regs.a[1] = bootnode; - CallLib (get_long(4), -270); /* Enqueue() */ +#endif + u = units; + while (u != 0) { + while (u->rootnode.next != &u->rootnode) { + a_inode *a = u->rootnode.next; + u->rootnode.next = a->next; + if (a->dirty && a->parent) + fsdb_dir_writeback (a->parent); + free (a->nname); + free (a->aname); + free (a); } - } else { - /* Call AddDosNode() for the constructed node */ - regs.a[0] = devicenode; - regs.d[0] = (ULONG)-1; - regs.a[1] = 0; - regs.d[1] = 0; /* Flags */ - CallLib (EXPANSION_explibbase, -150); /* AddDosNode() */ + u = u->next; } } - -void filesys_init(void) + +static uae_u32 filesys_diagentry (void) { - int i; - int firstdev = 1; - - /* Open expansion.lib */ - - EXPANSION_haveV36 = 0; - regs.d[0] = 36; - regs.a[1] = EXPANSION_explibname; - EXPANSION_explibbase = CallLib (get_long(4), -552); /* OpenLibrary() */ - if (EXPANSION_explibbase) - EXPANSION_haveV36 = 1; - else { - regs.d[0] = 0; - regs.a[1] = EXPANSION_explibname; - EXPANSION_explibbase = CallLib (get_long(4), -552); /* OpenLibrary() */ - } + uaecptr resaddr = m68k_areg (regs, 2) + 0x10; + uaecptr start = resaddr; + uaecptr residents, tmp; - filesys_parampacket = build_parmpacket(); + TRACE (("filesystem: diagentry called\n")); - /* re-use the same parameter packet to make each - * dos node, which will then get tweaked - */ + filesys_configdev = m68k_areg (regs, 3); - for(i = 0; i < num_units; i++) { - int is_hardfile = ui[i].volname == NULL; - if (is_hardfile && !EXPANSION_haveV36) { - fprintf(stderr, "Kickstart is older than 2.0, please mount hardfile manually.\n"); - continue; + do_put_mem_long ((uae_u32 *)(filesysory + 0x2100), EXPANSION_explibname); + do_put_mem_long ((uae_u32 *)(filesysory + 0x2104), filesys_configdev); + do_put_mem_long ((uae_u32 *)(filesysory + 0x2108), EXPANSION_doslibname); + do_put_mem_long ((uae_u32 *)(filesysory + 0x210c), current_mountinfo->num_units); + + uae_sem_init (&singlethread_int_sem, 0, 1); + if (ROM_hardfile_resid != 0) { + /* Build a struct Resident. This will set up and initialize + * the uae.device */ + put_word (resaddr + 0x0, 0x4AFC); + put_long (resaddr + 0x2, resaddr); + put_long (resaddr + 0x6, resaddr + 0x1A); /* Continue scan here */ + put_word (resaddr + 0xA, 0x8101); /* RTF_AUTOINIT|RTF_COLDSTART; Version 1 */ + put_word (resaddr + 0xC, 0x0305); /* NT_DEVICE; pri 05 */ + put_long (resaddr + 0xE, ROM_hardfile_resname); + put_long (resaddr + 0x12, ROM_hardfile_resid); + put_long (resaddr + 0x16, ROM_hardfile_init); /* calls filesys_init */ + } + resaddr += 0x1A; + tmp = resaddr; + + /* The good thing about this function is that it always gets called + * when we boot. So we could put all sorts of stuff that wants to be done + * here. + * We can simply add more Resident structures here. Although the Amiga OS + * only knows about the one at address DiagArea + 0x10, we scan for other + * Resident structures and call InitResident() for them at the end of the + * diag entry. */ + + resaddr = scsidev_startup(resaddr); + native2amiga_startup(); + + /* scan for Residents and return pointer to array of them */ + residents = resaddr; + while (tmp < residents && tmp > start) { + if (get_word (tmp) == 0x4AFC && + get_long (tmp + 0x2) == tmp) { + put_word (resaddr, 0x227C); /* movea.l #tmp,a1 */ + put_long (resaddr + 2, tmp); + put_word (resaddr + 6, 0x7200); /* moveq.l #0,d1 */ + put_long (resaddr + 8, 0x4EAEFF9A); /* jsr -$66(a6) ; InitResident */ + resaddr += 12; + tmp = get_long (tmp + 0x6); + } else { + tmp++; } - make_dev(filesys_parampacket, i, is_hardfile, 1); } + put_word (resaddr, 0x7001); /* moveq.l #1,d0 */ + put_word (resaddr + 2, RTS); - regs.a[1] = EXPANSION_explibbase; - CallLib (get_long(4), -414); /* CloseLibrary() */ - EXPANSION_explibbase = 0; + m68k_areg (regs, 0) = residents; + return 1; } -void filesys_install(void) +/* Remember a pointer AmigaOS gave us so we can later use it to identify + * which unit a given startup message belongs to. */ +static uae_u32 filesys_dev_remember (void) +{ + int unit_no = m68k_dreg (regs, 6); + uaecptr devicenode = m68k_areg (regs, 3); + + current_mountinfo->ui[unit_no].startup = get_long (devicenode + 28); + return devicenode; +} + +/* Fill in per-unit fields of a parampacket */ +static uae_u32 filesys_dev_storeinfo (void) +{ + UnitInfo *uip = current_mountinfo->ui; + int unit_no = m68k_dreg (regs, 6); + uaecptr parmpacket = m68k_areg (regs, 0); + + put_long (parmpacket, current_mountinfo->ui[unit_no].devname_amiga); + put_long (parmpacket + 4, is_hardfile (current_mountinfo, unit_no) ? ROM_hardfile_resname : fsdevname); + put_long (parmpacket + 8, uip[unit_no].devno); + put_long (parmpacket + 12, 0); /* Device flags */ + put_long (parmpacket + 16, 16); /* Env. size */ + put_long (parmpacket + 20, uip[unit_no].hf.blocksize >> 2); /* longwords per block */ + put_long (parmpacket + 24, 0); /* unused */ + put_long (parmpacket + 28, uip[unit_no].hf.surfaces); /* heads */ + put_long (parmpacket + 32, 0); /* unused */ + put_long (parmpacket + 36, uip[unit_no].hf.secspertrack); /* sectors per track */ + put_long (parmpacket + 40, uip[unit_no].hf.reservedblocks); /* reserved blocks */ + put_long (parmpacket + 44, 0); /* unused */ + put_long (parmpacket + 48, 0); /* interleave */ + put_long (parmpacket + 52, 0); /* lowCyl */ + put_long (parmpacket + 56, uip[unit_no].hf.nrcyls - 1); /* hiCyl */ + put_long (parmpacket + 60, 50); /* Number of buffers */ + put_long (parmpacket + 64, 0); /* Buffer mem type */ + put_long (parmpacket + 68, 0x7FFFFFFF); /* largest transfer */ + put_long (parmpacket + 72, ~1); /* addMask (?) */ + put_long (parmpacket + 76, (uae_u32)-1); /* bootPri */ + put_long (parmpacket + 80, 0x444f5300); /* DOS\0 */ + put_long (parmpacket + 84, 0); /* pad */ + + return is_hardfile (current_mountinfo, unit_no); +} + +void filesys_install (void) { int i; - CPTR loop; + uaecptr loop; - ROM_filesys_resname = ds("UAEunixfs.resource"); - ROM_filesys_resid = ds("UAE unixfs 0.2"); + TRACE (("Installing filesystem\n")); - fsdevname = ds("uae.device"); /* does not really exist */ + ROM_filesys_resname = ds("UAEunixfs.resource"); + ROM_filesys_resid = ds("UAE unixfs 0.4"); - for(i = 0; i < num_units; i++) { - ui[i].devno = get_new_device(&ui[i].devname, &ui[i].devname_amiga); - } + fsdevname = ds ("uae.device"); /* does not really exist */ ROM_filesys_diagentry = here(); - calltrap2(deftrap(filesys_diagentry)); dw(RTS); - - /* align */ + calltrap (deftrap(filesys_diagentry)); + dw(0x4ED0); /* JMP (a0) - jump to code that inits Residents */ + + loop = here (); + /* Special trap for the assembly make_dev routine */ + org (0xF0FF20); + calltrap (deftrap (filesys_dev_remember)); + dw (RTS); + + org (0xF0FF28); + calltrap (deftrap (filesys_dev_storeinfo)); + dw (RTS); + + org (0xF0FF30); + calltrap (deftrap (filesys_handler)); + dw (RTS); + + org (0xF0FF40); + calltrap (deftrap (startup_handler)); + dw (RTS); + + org (0xF0FF50); + calltrap (deftrap (exter_int_helper)); + dw (RTS); + + org (loop); +} + +void filesys_install_code (void) +{ align(4); - /* Fake seglist */ - dl(16); - filesysseglist = here(); - dl(0); /* NextSeg */ - - /* start of code */ - - /* I don't trust calling functions that Wait() directly, - * so here's a little bit of 68000 code to receive and send our - * DosPackets - */ - dw(0x2c79); dl(4); /* move.l $4,a6 */ - dw(0x2279); dl(0); /* move.l 0,a1 */ - dw(0x4eae); dw(0xfeda); /* jsr FindTask(a6) */ - dw(0x2040); /* move.l d0,a0 */ - dw(0x4be8); dw(0x005c); /* lea.l pr_MsgPort(a0),a5 */ - /* loop: */ - loop = here(); - dw(0x204d); /* move.l a5,a0 */ - dw(0x4eae); dw(0xfe80); /* jsr WaitPort(a6) */ - dw(0x204d); /* move.l a5,a0 */ - dw(0x4eae); dw(0xfe8c); /* jsr GetMsg(a6) */ - dw(0x2840); /* move.l d0,a4 */ - dw(0x286c); dw(10); /* move.l LN_NAME(a4),a4 */ - calltrap2(deftrap(filesys_handler)); - dw(0x226c); dw(0); /* move.l dp_Link(a4),a1 */ - dw(0x206c); dw(4); /* move.l dp_Port(a4),a0 */ - dw(0x294d); dw(4); /* move.l a5,dp_Port(a4) */ - dw(0x4eae); dw(0xfe92); /* jsr PutMsg(a6) */ - dw(0x4ef9); dl(loop); /* jmp loop */ + /* The last offset comes from the code itself, look for it near the top. */ + EXPANSION_bootcode = here () + 8 + 0x14; + /* Ouch. Make sure this is _always_ a multiple of two bytes. */ + filesys_initcode = here() + 8 + 0x28; + db(0x00); db(0x00); db(0x00); db(0x10); db(0x00); db(0x00); db(0x00); db(0x00); + db(0x60); db(0x00); db(0x01); db(0xd4); db(0x00); db(0x00); db(0x01); db(0x3e); + db(0x00); db(0x00); db(0x00); db(0x28); db(0x00); db(0x00); db(0x00); db(0xbc); + db(0x00); db(0x00); db(0x00); db(0x14); db(0x43); db(0xfa); db(0x03); db(0x11); + db(0x4e); db(0xae); db(0xff); db(0xa0); db(0x20); db(0x40); db(0x20); db(0x28); + db(0x00); db(0x16); db(0x20); db(0x40); db(0x4e); db(0x90); db(0x4e); db(0x75); + db(0x48); db(0xe7); db(0xff); db(0xfe); db(0x2c); db(0x78); db(0x00); db(0x04); + db(0x2a); db(0x79); db(0x00); db(0xf0); db(0xff); db(0xfc); db(0x43); db(0xfa); + db(0x02); db(0xfb); db(0x70); db(0x24); db(0x7a); db(0x00); db(0x4e); db(0xae); + db(0xfd); db(0xd8); db(0x4a); db(0x80); db(0x66); db(0x0c); db(0x43); db(0xfa); + db(0x02); db(0xeb); db(0x70); db(0x00); db(0x7a); db(0x01); db(0x4e); db(0xae); + db(0xfd); db(0xd8); db(0x28); db(0x40); db(0x70); db(0x58); db(0x72); db(0x01); + db(0x4e); db(0xae); db(0xff); db(0x3a); db(0x26); db(0x40); db(0x7e); db(0x54); + db(0x27); db(0xb5); db(0x78); db(0x00); db(0x78); db(0x00); db(0x59); db(0x87); + db(0x64); db(0xf6); db(0x7c); db(0x00); db(0xbc); db(0xad); db(0x01); db(0x0c); + db(0x64); db(0x14); db(0x20); db(0x4b); db(0x48); db(0xe7); db(0x02); db(0x10); + db(0x7e); db(0x01); db(0x61); db(0x00); db(0x00); db(0xc2); db(0x4c); db(0xdf); + db(0x08); db(0x40); db(0x52); db(0x86); db(0x60); db(0xe6); db(0x2c); db(0x78); + db(0x00); db(0x04); db(0x22); db(0x4c); db(0x4e); db(0xae); db(0xfe); db(0x62); + db(0x61); db(0x00); db(0x00); db(0x7c); db(0x2c); db(0x78); db(0x00); db(0x04); + db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x80); db(0x72); db(0x03); + db(0x74); db(0xf6); db(0x20); db(0x7c); db(0x00); db(0x20); db(0x00); db(0x00); + db(0x90); db(0x88); db(0x65); db(0x0a); db(0x67); db(0x08); db(0x78); db(0x00); + db(0x22); db(0x44); db(0x4e); db(0xae); db(0xfd); db(0x96); db(0x4c); db(0xdf); + db(0x7f); db(0xff); db(0x4e); db(0x75); db(0x48); db(0xe7); db(0x00); db(0x20); + db(0x70); db(0x00); db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x50); + db(0x4a); db(0x80); db(0x67); db(0x3c); db(0x2c); db(0x78); db(0x00); db(0x04); + db(0x70); db(0x02); db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x50); + db(0x0c); db(0x80); db(0x00); db(0x00); db(0x00); db(0x01); db(0x6d); db(0x1e); + db(0x6e); db(0x06); db(0x4e); db(0xae); db(0xfe); db(0x92); db(0x60); db(0xe8); + db(0x0c); db(0x80); db(0x00); db(0x00); db(0x00); db(0x02); db(0x6e); db(0x08); + db(0x20); db(0x01); db(0x4e); db(0xae); db(0xfe); db(0xbc); db(0x60); db(0xd8); + db(0x4e); db(0xae); db(0xfe); db(0x86); db(0x60); db(0xd2); db(0x70); db(0x04); + db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x50); db(0x70); db(0x01); + db(0x4c); db(0xdf); db(0x04); db(0x00); db(0x4e); db(0x75); db(0x2c); db(0x78); + db(0x00); db(0x04); db(0x70); db(0x1a); db(0x22); db(0x3c); db(0x00); db(0x01); + db(0x00); db(0x01); db(0x4e); db(0xae); db(0xff); db(0x3a); db(0x22); db(0x40); + db(0x41); db(0xfa); db(0x01); db(0xf6); db(0x23); db(0x48); db(0x00); db(0x0a); + db(0x41); db(0xfa); db(0xff); db(0x92); db(0x23); db(0x48); db(0x00); db(0x0e); + db(0x41); db(0xfa); db(0xff); db(0x8a); db(0x23); db(0x48); db(0x00); db(0x12); + db(0x70); db(0x0d); db(0x4e); db(0xee); db(0xff); db(0x58); db(0x2a); db(0x79); + db(0x00); db(0xf0); db(0xff); db(0xfc); db(0x4e); db(0xb9); db(0x00); db(0xf0); + db(0xff); db(0x28); db(0x26); db(0x00); db(0xc0); db(0x85); db(0x66); db(0x00); + db(0xff); db(0x6a); db(0x2c); db(0x4c); db(0x4e); db(0xae); db(0xff); db(0x70); + db(0x26); db(0x40); db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x20); + db(0x70); db(0x00); db(0x27); db(0x40); db(0x00); db(0x08); db(0x27); db(0x40); + db(0x00); db(0x10); db(0x27); db(0x40); db(0x00); db(0x20); db(0x4a); db(0x83); + db(0x66); db(0x1c); db(0x27); db(0x7c); db(0x00); db(0x00); db(0x0f); db(0xa0); + db(0x00); db(0x14); db(0x43); db(0xfa); db(0xfe); db(0x80); db(0x20); db(0x09); + db(0xe4); db(0x88); db(0x27); db(0x40); db(0x00); db(0x20); db(0x27); db(0x7c); + db(0xff); db(0xff); db(0xff); db(0xff); db(0x00); db(0x24); db(0x4a); db(0x87); + db(0x67); db(0x36); db(0x2c); db(0x78); db(0x00); db(0x04); db(0x70); db(0x14); + db(0x72); db(0x00); db(0x4e); db(0xae); db(0xff); db(0x3a); db(0x22); db(0x40); + db(0x70); db(0x00); db(0x22); db(0x80); db(0x23); db(0x40); db(0x00); db(0x04); + db(0x33); db(0x40); db(0x00); db(0x0e); db(0x30); db(0x3c); db(0x10); db(0xff); + db(0x90); db(0x06); db(0x33); db(0x40); db(0x00); db(0x08); db(0x23); db(0x6d); + db(0x01); db(0x04); db(0x00); db(0x0a); db(0x23); db(0x4b); db(0x00); db(0x10); + db(0x41); db(0xec); db(0x00); db(0x4a); db(0x4e); db(0xee); db(0xfe); db(0xf2); + db(0x20); db(0x4b); db(0x72); db(0x00); db(0x22); db(0x41); db(0x70); db(0xff); + db(0x2c); db(0x4c); db(0x4e); db(0xee); db(0xff); db(0x6a); db(0x2c); db(0x78); + db(0x00); db(0x04); db(0x70); db(0x00); db(0x22); db(0x40); db(0x4e); db(0xae); + db(0xfe); db(0xda); db(0x20); db(0x40); db(0x4b); db(0xe8); db(0x00); db(0x5c); + db(0x43); db(0xfa); db(0x01); db(0x3d); db(0x70); db(0x00); db(0x4e); db(0xae); + db(0xfd); db(0xd8); db(0x24); db(0x40); db(0x20); db(0x3c); db(0x00); db(0x00); + db(0x00); db(0x9d); db(0x22); db(0x3c); db(0x00); db(0x01); db(0x00); db(0x01); + db(0x4e); db(0xae); db(0xff); db(0x3a); db(0x26); db(0x40); db(0x7c); db(0x00); + db(0x26); db(0x86); db(0x27); db(0x46); db(0x00); db(0x04); db(0x27); db(0x46); + db(0x00); db(0x08); db(0x7a); db(0x00); db(0x20); db(0x4d); db(0x4e); db(0xae); + db(0xfe); db(0x80); db(0x20); db(0x4d); db(0x4e); db(0xae); db(0xfe); db(0x8c); + db(0x28); db(0x40); db(0x26); db(0x2c); db(0x00); db(0x0a); db(0x70); db(0x00); + db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x40); db(0x60); db(0x76); + db(0x20); db(0x4d); db(0x4e); db(0xae); db(0xfe); db(0x80); db(0x20); db(0x4d); + db(0x4e); db(0xae); db(0xfe); db(0x8c); db(0x28); db(0x40); db(0x26); db(0x2c); + db(0x00); db(0x0a); db(0x66); db(0x38); db(0x70); db(0x01); db(0x4e); db(0xb9); + db(0x00); db(0xf0); db(0xff); db(0x50); db(0x45); db(0xeb); db(0x00); db(0x04); + db(0x20); db(0x52); db(0x20); db(0x08); db(0x67); db(0xda); db(0x22); db(0x50); + db(0x20); db(0x40); db(0x20); db(0x28); db(0x00); db(0x04); db(0x6a); db(0x16); + db(0x48); db(0xe7); db(0x00); db(0xc0); db(0x28); db(0x68); db(0x00); db(0x0a); + db(0x61); db(0x42); db(0x53); db(0x85); db(0x4c); db(0xdf); db(0x03); db(0x00); + db(0x24); db(0x89); db(0x20); db(0x49); db(0x60); db(0xdc); db(0x24); db(0x48); + db(0x20); db(0x49); db(0x60); db(0xd6); db(0x0c); db(0x85); db(0x00); db(0x00); + db(0x00); db(0x14); db(0x65); db(0x00); db(0x00); db(0x0a); db(0x70); db(0x01); + db(0x29); db(0x40); db(0x00); db(0x04); db(0x60); db(0x0e); db(0x61); db(0x2a); + db(0x4e); db(0xb9); db(0x00); db(0xf0); db(0xff); db(0x30); db(0x4a); db(0x80); + db(0x67); db(0x0c); db(0x52); db(0x85); db(0x28); db(0xab); db(0x00); db(0x04); + db(0x27); db(0x4c); db(0x00); db(0x04); db(0x60); db(0x8a); db(0x28); db(0x43); + db(0x61); db(0x02); db(0x60); db(0x84); db(0x22); db(0x54); db(0x20); db(0x6c); + db(0x00); db(0x04); db(0x29); db(0x4d); db(0x00); db(0x04); db(0x4e); db(0xee); + db(0xfe); db(0x92); db(0x2f); db(0x05); db(0x7a); db(0xfc); db(0x24); db(0x53); + db(0x2e); db(0x0a); db(0x22); db(0x0a); db(0x67); db(0x00); db(0x00); db(0x0c); + db(0x52); db(0x85); db(0x67); db(0x1e); db(0x22); db(0x4a); db(0x24); db(0x52); + db(0x60); db(0xf0); db(0x52); db(0x85); db(0x67); db(0x3c); db(0x24); db(0x47); + db(0x70); db(0x18); db(0x72); db(0x01); db(0x4e); db(0xae); db(0xff); db(0x3a); + db(0x52); db(0x46); db(0x24); db(0x40); db(0x24); db(0x87); db(0x2e); db(0x0a); + db(0x60); db(0xe8); db(0x20); db(0x12); db(0x67); db(0x24); db(0x20); db(0x40); + db(0x20); db(0x10); db(0x67); db(0x1e); db(0x20); db(0x40); db(0x20); db(0x10); + db(0x67); db(0x18); db(0x70); db(0x00); db(0x22); db(0x80); db(0x22); db(0x4a); + db(0x24); db(0x51); db(0x70); db(0x18); db(0x4e); db(0xae); db(0xff); db(0x2e); + db(0x06); db(0x86); db(0x00); db(0x01); db(0x00); db(0x00); db(0x20); db(0x0a); + db(0x66); db(0xec); db(0x26); db(0x87); db(0x2a); db(0x1f); db(0x4e); db(0x75); + db(0x55); db(0x41); db(0x45); db(0x20); db(0x66); db(0x69); db(0x6c); db(0x65); + db(0x73); db(0x79); db(0x73); db(0x74); db(0x65); db(0x6d); db(0x00); db(0x64); + db(0x6f); db(0x73); db(0x2e); db(0x6c); db(0x69); db(0x62); db(0x72); db(0x61); + db(0x72); db(0x79); db(0x00); db(0x65); db(0x78); db(0x70); db(0x61); db(0x6e); + db(0x73); db(0x69); db(0x6f); db(0x6e); db(0x2e); db(0x6c); db(0x69); db(0x62); + db(0x72); db(0x61); db(0x72); db(0x79); db(0x00); db(0x00); db(0x00); db(0x00); + db(0x00); db(0x00); db(0x03); db(0xf2); }