--- uae/src/filesys.c 2018/04/24 16:44:58 1.1.1.6 +++ uae/src/filesys.c 2018/04/24 17:02:12 1.1.1.13 @@ -28,17 +28,20 @@ #include "sysdeps.h" #include "config.h" +#include "threaddep/thread.h" #include "options.h" #include "uae.h" #include "memory.h" #include "custom.h" -#include "readcpu.h" +#include "events.h" #include "newcpu.h" #include "filesys.h" #include "autoconf.h" #include "compiler.h" #include "fsusage.h" -#include "threaddep/penguin.h" +#include "native2amiga.h" +#include "scsidev.h" +#include "fsdb.h" /* #define TRACING_ENABLED */ #ifdef TRACING_ENABLED @@ -49,29 +52,6 @@ #define DUMPLOCK(u,x) #endif -#define MAKE_CASE_INSENSITIVE - -/* 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_INVALID_LOCK 211 -#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_IS_FULL 221 -#define ERROR_DELETE_PROTECTED 222 -#define ERROR_WRITE_PROTECTED 223 -#define ERROR_READ_PROTECTED 224 -#define ERROR_NO_MORE_ENTRIES 232 -#define ERROR_NOT_IMPLEMENTED 236 - -#ifndef DONT_HAVE_POSIX static long dos_errno(void) { int e = errno; @@ -80,7 +60,7 @@ static long dos_errno(void) 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 ENOENT: return ERROR_OBJECT_NOT_AROUND; case ENOTDIR: return ERROR_OBJECT_WRONG_TYPE; case ENOSPC: return ERROR_DISK_IS_FULL; case EBUSY: return ERROR_OBJECT_IN_USE; @@ -102,7 +82,6 @@ static long dos_errno(void) return ERROR_NOT_IMPLEMENTED; } } -#endif /* * This _should_ be no issue for us, but let's rather use a guaranteed @@ -133,20 +112,6 @@ static uae_u32 fsdevname, filesys_config #define FS_STARTUP 0 #define FS_GO_DOWN 1 -/* AmigaOS "keys" */ -struct a_inode { - struct a_inode *next, *prev; - struct a_inode *parent; - struct a_inode *child, *sibling; /* @@@ implement hash tables or something to make this efficient */ - char *aname; - char *nname; - int amigaos_mode; - int shlock; - uae_u32 uniq; - unsigned int dir:1; - unsigned int elock:1; -}; - typedef struct { char *devname; /* device name, e.g. UAE0: */ uaecptr devname_amiga; @@ -160,7 +125,7 @@ typedef struct { /* Threading stuff */ smp_comm_pipe *unit_pipe, *back_pipe; - penguin_id tid; + uae_thread_id tid; struct _unit *volatile self; /* Reset handling */ uae_sem_t reset_sync_sem; @@ -384,10 +349,11 @@ void write_filesys_config (struct uaedev char *str; str = cfgfile_subst_path (default_path, unexpanded, uip[i].rootdir); if (uip[i].volname != 0) { - fprintf (f, "filesystem=%s,%s:%s", uip[i].readonly ? "ro" : "rw", + 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", uip[i].hf.secspertrack, + fprintf (f, "hardfile=%s,%d,%d,%d,%d,%s\n", + uip[i].readonly ? "ro" : "rw", uip[i].hf.secspertrack, uip[i].hf.surfaces, uip[i].hf.reservedblocks, 512, str); } free (str); @@ -417,7 +383,7 @@ struct uaedev_mount_info *dup_mountinfo if (uip->rootdir) uip->rootdir = my_strdup (uip->rootdir); if (uip->hf.fd) - uip->hf.fd = fdopen (fileno (uip->hf.fd), uip->readonly ? "rb" : "r+b"); + uip->hf.fd = fdopen ( dup (fileno (uip->hf.fd)), uip->readonly ? "rb" : "r+b"); } return i2; } @@ -453,6 +419,12 @@ struct hardfiledata *get_hardfile_data ( #define DOS_TRUE ((unsigned long)-1L) #define DOS_FALSE (0L) +/* Passed as type to Lock() */ +#define SHARED_LOCK -2 /* File is readable by others */ +#define ACCESS_READ -2 /* Synonym */ +#define EXCLUSIVE_LOCK -1 /* No other access allowed */ +#define ACCESS_WRITE -1 /* Synonym */ + /* packet types */ #define ACTION_CURRENT_VOLUME 7 #define ACTION_LOCATE_OBJECT 8 @@ -503,13 +475,13 @@ struct hardfiledata *get_hardfile_data ( typedef struct { uae_u32 uniq; - struct a_inode *aino; + a_inode *aino; DIR* dir; } ExamineKey; typedef struct key { struct key *next; - struct a_inode *aino; + a_inode *aino; uae_u32 uniq; int fd; off_t file_pos; @@ -553,9 +525,9 @@ typedef struct _unit { uae_u32 key_uniq; uae_u32 a_uniq; - struct a_inode rootnode; + a_inode rootnode; unsigned long aino_cache_size; - struct a_inode *aino_hash[MAX_AINO_HASH]; + a_inode *aino_hash[MAX_AINO_HASH]; unsigned long nr_cache_hits; unsigned long nr_cache_lookups; } Unit; @@ -626,63 +598,7 @@ find_unit (uaecptr port) return u; } - -static int get_file_attrs (struct a_inode *aino) -{ - struct stat statbuf; - if (-1 == stat (aino->nname, &statbuf)) - return dos_errno (); - aino->dir = S_ISDIR (statbuf.st_mode) ? 1 : 0; - aino->amigaos_mode = -#if !defined __DOS__ && !defined _WIN32 - (S_IXUSR & statbuf.st_mode ? 0 : A_FIBF_EXECUTE) | -#endif - (S_IWUSR & statbuf.st_mode ? 0 : A_FIBF_WRITE) | -#if 0 /* This makes it impossible to overwrite those. */ - (S_IWUSR & statbuf.st_mode ? 0 : A_FIBF_DELETE) | -#endif - (S_IRUSR & statbuf.st_mode ? 0 : A_FIBF_READ); - return 0; -}; - -static int set_file_attrs (struct a_inode *aino, int mask) -{ - struct stat statbuf; - int mode; - - if (-1 == stat(aino->nname, &statbuf)) - return ERROR_OBJECT_NOT_FOUND; - - mode = statbuf.st_mode; -#ifdef __unix - /* Unix dirs behave differently than AmigaOS ones. Just allow anything. */ - if (aino->dir) { - mask = 0; - } -#endif - if (mask & A_FIBF_READ) - mode &= ~S_IRUSR; - else - mode |= S_IRUSR; - - if ((mask & A_FIBF_WRITE) != 0 || (mask & A_FIBF_DELETE) != 0) - mode &= ~S_IWUSR; - else - mode |= S_IWUSR; - - if (mask & A_FIBF_EXECUTE) - mode &= ~S_IXUSR; - else - mode |= S_IXUSR; - - if (-1 == chmod(aino->nname, mode)) - return dos_errno(); - - aino->amigaos_mode = mask; - - return 0; -} - + static void prepare_for_open (char *name) { #if 0 @@ -700,7 +616,7 @@ static void prepare_for_open (char *name #endif } -static void de_recycle_aino (Unit *unit, struct a_inode *aino) +static void de_recycle_aino (Unit *unit, a_inode *aino) { if (aino->next == 0 || aino == &unit->rootnode) return; @@ -710,47 +626,50 @@ static void de_recycle_aino (Unit *unit, unit->aino_cache_size--; } -static void recycle_aino (Unit *unit, struct a_inode *aino) +static void dispose_aino (Unit *unit, a_inode **aip, a_inode *aino) { - if (aino->dir || aino->shlock > 0 || aino->elock || aino == &unit->rootnode) + 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 *new_aino) +{ + if (new_aino->dir || new_aino->shlock > 0 + || new_aino->elock || new_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) { - struct a_inode **aip; + a_inode **aip; aip = &unit->rootnode.prev->parent->child; for (;;) { - aino = *aip; + a_inode *aino = *aip; if (aino == 0) break; if (aino->next == 0) aip = &aino->sibling; else { - int hash = aino->uniq % MAX_AINO_HASH; - if (unit->aino_hash[hash] == aino) - unit->aino_hash[hash] = 0; - - aino->next->prev = aino->prev; - aino->prev->next = aino->next; - *aip = aino->sibling; - i++; - if (aino->shlock > 0 || aino->elock) write_log ("panic: freeing locked a_inode!\n"); - free (aino->nname); - free (aino->aname); - free (aino); + de_recycle_aino (unit, aino); + dispose_aino (unit, aip, aino); + i++; } } } @@ -761,27 +680,34 @@ static void recycle_aino (Unit *unit, st write_log (buffer); } #endif - unit->aino_cache_size -= i; } + + /* Chain it into circular list. */ + new_aino->next = unit->rootnode.next; + new_aino->prev = &unit->rootnode; + new_aino->prev->next = new_aino; + new_aino->next->prev = new_aino; + unit->aino_cache_size++; } -static void update_child_names (Unit *unit, struct a_inode *a, struct a_inode *parent) +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; - + char dirsep[2] = { FSDB_DIR_SEPARATOR, '\0' }; + a->parent = parent; - name_start = strrchr (a->nname, '/'); + name_start = strrchr (a->nname, FSDB_DIR_SEPARATOR); 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, dirsep); strcat (new_name, name_start); free (a->nname); a->nname = new_name; @@ -791,20 +717,22 @@ static void update_child_names (Unit *un } } -static void move_aino_children (Unit *unit, struct a_inode *from, struct a_inode *to) +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, struct a_inode *aino) +static void delete_aino (Unit *unit, a_inode *aino) { - struct a_inode **aip; + 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) @@ -813,19 +741,13 @@ static void delete_aino (Unit *unit, str write_log ("Couldn't delete aino.\n"); return; } - hash = aino->uniq % MAX_AINO_HASH; - if (unit->aino_hash[hash] == aino) - unit->aino_hash[hash] = 0; - *aip = aino->sibling; - free (aino->nname); - free (aino->aname); - free (aino); + dispose_aino (unit, aip, aino); } -static struct a_inode *lookup_sub (struct a_inode *dir, uae_u32 uniq) +static a_inode *lookup_sub (a_inode *dir, uae_u32 uniq) { - struct a_inode **cp = &dir->child; - struct a_inode *c, *retval; + a_inode **cp = &dir->child; + a_inode *c, *retval; for (;;) { c = *cp; @@ -837,7 +759,7 @@ static struct a_inode *lookup_sub (struc break; } if (c->dir) { - struct a_inode *a = lookup_sub (c, uniq); + a_inode *a = lookup_sub (c, uniq); if (a != 0) { retval = a; break; @@ -851,9 +773,9 @@ static struct a_inode *lookup_sub (struc return retval; } -static struct a_inode *lookup_aino (Unit *unit, uae_u32 uniq) +static a_inode *lookup_aino (Unit *unit, uae_u32 uniq) { - struct a_inode *a; + a_inode *a; int hash = uniq % MAX_AINO_HASH; if (uniq == 0) @@ -868,35 +790,82 @@ static struct a_inode *lookup_aino (Unit return a; } +char *build_nname (const char *d, const char *n) +{ + 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; +} + +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, struct a_inode *base, char *rel) + * a name that we can use on the native filesystem. */ +static char *get_nname (Unit *unit, a_inode *base, char *rel, + char **modified_rel) { - DIR* dir; - struct dirent *de; + char *found; char *p = 0; - /* Don't allow Unix "." and "..", which confuse AmigaOS and let us - * escape out of the mount point. */ - if (rel[0] == '.' - && (rel[1] == '\0' - || (rel[1] == '.' && rel[2] == '\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; - dir = opendir (base->nname); - if (dir) { - struct dirent de_space; - while ((de = my_readdir (dir, &de_space))) { - if (strcasecmp (de->d_name, rel) == 0) - break; - } - if (de) { - p = (char *)xmalloc (strlen (de->d_name) + strlen (base->nname) + 2); - strcpy (p, base->nname); - strcat (p, "/"); - strcat (p, de->d_name); - } - closedir (dir); + /* 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; } @@ -912,41 +881,13 @@ static char *get_nname (Unit *unit, stru * figure out that this is supposed to be the file "foobar.inf". * DOS sucks... */ -static char *get_aname (Unit *unit, struct a_inode *base, char *rel) +static char *get_aname (Unit *unit, a_inode *base, char *rel) { - char *p = 0; - - p = (char *)xmalloc (strlen (rel) + strlen (base->aname) + 2); - strcpy (p, base->aname); - strcat (p, "/"); - strcat (p, rel); - - return p; + return my_strdup (rel); } -static struct a_inode *new_child_aino (Unit *unit, struct a_inode *base, char *rel, int force) +static void init_child_aino (Unit *unit, a_inode *base, a_inode *aino) { - char *nn; - struct a_inode *aino; - - nn = get_nname (unit, base, rel); - if (nn == 0 && !force) - return 0; - else if (nn != 0) - force = 0; - - aino = (struct a_inode *)malloc (sizeof (struct a_inode)); - if (aino == 0) - return 0; - aino->aname = my_strdup (rel); - if (nn == 0) { - nn = (char *)xmalloc (strlen (base->nname) + strlen (rel) + 2); - strcpy (nn, base->nname); - strcat (nn, "/"); - strcat (nn, rel); - free (rel); - } - aino->nname = nn; aino->uniq = ++unit->a_uniq; if (unit->a_uniq == 0xFFFFFFFF) { write_log ("Running out of a_inodes (prepare for big trouble)!\n"); @@ -954,31 +895,76 @@ static struct a_inode *new_child_aino (U 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; +} - if (force == 2) { - aino->dir = 1; - aino->amigaos_mode = 0; - } else if (force == 1) { - aino->dir = 0; - aino->amigaos_mode = 0; - } else { - get_file_attrs (aino); +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 *) xcalloc (sizeof (a_inode), 1); + 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, normal\n", aino->uniq)); + TRACE(("created aino %x, lookup\n", aino->uniq)); return aino; } -static struct a_inode *lookup_child_aino (Unit *unit, struct a_inode *base, char *rel, uae_u32 *err) +static a_inode *create_child_aino (Unit *unit, a_inode *base, char *rel, int isdir) { - struct a_inode *c = base->child; + a_inode *aino = (a_inode *) xcalloc (sizeof (a_inode), 1); + 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) { @@ -988,70 +974,65 @@ static struct a_inode *lookup_child_aino while (c != 0) { int l1 = strlen (c->aname); - if (l0 <= l1 && strcasecmp (rel, c->aname + l1 - l0) == 0 + 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, 0); + c = new_child_aino (unit, base, rel); if (c == 0) - *err = ERROR_OBJECT_NOT_FOUND; + *err = ERROR_OBJECT_NOT_AROUND; return c; } -/* Different version because this one compares with the nname */ -static struct a_inode *lookup_child_aino_for_exnext (Unit *unit, struct a_inode *base, char *rel, uae_u32 *err) +/* 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) { - struct a_inode *c = base->child; + a_inode *c = base->child; int l0 = strlen (rel); *err = 0; while (c != 0) { int l1 = strlen (c->nname); - if (l0 <= l1 && strcasecmp (rel, c->nname + l1 - l0) == 0 - && (l0 == l1 || c->nname[l1-l0-1] == '/')) + /* 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 = (struct a_inode *)malloc (sizeof (struct a_inode)); - if (c == 0) - *err = ERROR_NO_FREE_STORE; - else { - c->nname = (char *)xmalloc (strlen (base->nname) + l0 + 2); - strcpy (c->nname, base->nname); - strcat (c->nname, "/"); - strcat (c->nname, rel); + 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->uniq = ++unit->a_uniq; - if (unit->a_uniq == 0xFFFFFFFF) { - write_log ("Running out of a_inodes (prepare for big trouble)!\n"); - } - c->shlock = 0; - c->elock = 0; - - /* Update tree structure */ - c->parent = base; - c->child = 0; - c->sibling = base->child; - base->child = c; - c->next = c->prev = 0; - - get_file_attrs (c); - recycle_aino (unit, c); - TRACE(("created aino %x, exnext\n", c->uniq)); + 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 struct a_inode *get_aino (Unit *unit, struct a_inode *base, const char *rel, uae_u32 *err) +static a_inode *get_aino (Unit *unit, a_inode *base, const char *rel, uae_u32 *err) { char *tmp; char *p; - struct a_inode *curr; + a_inode *curr; int i; *err = 0; @@ -1067,14 +1048,14 @@ static struct a_inode *get_aino (Unit *u p = tmp; curr = base; - while(*p) { + while (*p) { /* start with a slash? go up a level. */ if (*p == '/') { if (curr->parent != 0) curr = curr->parent; p++; } else { - struct a_inode *next; + a_inode *next; char *component_end; component_end = strchr (p, '/'); @@ -1083,7 +1064,7 @@ static struct a_inode *get_aino (Unit *u 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) + if (*err != ERROR_OBJECT_NOT_AROUND || component_end != 0) curr = 0; /* ? what error is appropriate? */ break; @@ -1105,8 +1086,8 @@ 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 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; @@ -1139,13 +1120,13 @@ static uae_u32 startup_handler (void) } uinfo = current_mountinfo->ui + i; - unit = (Unit *) xmalloc (sizeof (Unit)); + unit = (Unit *) xcalloc (sizeof (Unit), 1); unit->next = units; units = unit; uinfo->self = unit; unit->volume = 0; - unit->port = m68k_areg(regs, 5); + unit->port = m68k_areg (regs, 5); unit->unit = unit_num++; unit->ui.devname = uinfo->devname; @@ -1177,6 +1158,8 @@ static uae_u32 startup_handler (void) unit->rootnode.amigaos_mode = 0; unit->rootnode.shlock = 0; unit->rootnode.elock = 0; + unit->rootnode.comment = 0; + unit->rootnode.has_dbentry = 0; unit->aino_cache_size = 0; for (i = 0; i < MAX_AINO_HASH; i++) unit->aino_hash[i] = 0; @@ -1187,7 +1170,7 @@ static uae_u32 startup_handler (void) /* fill in our process in the device node */ put_long ((get_long (pkt + dp_Arg3) << 2) + 8, unit->port); - unit->dosbase = m68k_areg(regs, 2); + unit->dosbase = m68k_areg (regs, 2); /* make new volume */ unit->volume = m68k_areg (regs, 3) + 32; @@ -1213,13 +1196,16 @@ static uae_u32 startup_handler (void) 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 (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 (pkt + dp_Res1, DOS_TRUE); + + fsdb_clean_dir (&unit->rootnode); + return 0; } @@ -1236,15 +1222,15 @@ do_info (Unit *unit, dpacket packet, uae 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_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); } @@ -1278,7 +1264,7 @@ static void free_key (Unit *unit, Key *k } if (k->fd >= 0) - close(k->fd); + close (k->fd); free(k); } @@ -1318,16 +1304,16 @@ static Key *new_key (Unit *unit) static void dumplock (Unit *unit, uaecptr lock) { - struct a_inode *a; + a_inode *a; TRACE(("LOCK: 0x%lx", lock)); if (!lock) { TRACE(("\n")); return; } 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))); + 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!")); @@ -1337,12 +1323,12 @@ dumplock (Unit *unit, uaecptr lock) TRACE((" }\n")); } -static struct a_inode *find_aino (Unit *unit, uaecptr lock, const char *name, uae_u32 *err) +static a_inode *find_aino (Unit *unit, uaecptr lock, const char *name, uae_u32 *err) { - struct a_inode *a; + a_inode *a; if (lock) { - struct a_inode *olda = lookup_aino (unit, get_long(lock + 4)); + 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); @@ -1369,14 +1355,14 @@ static uaecptr make_lock (Unit *unit, ua put_long (unit->locklist, get_long (lock)); lock += 4; - put_long(lock + 4, 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(unit, lock); return lock; @@ -1387,18 +1373,18 @@ static void free_lock (Unit *unit, uaecp 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 { - uaecptr current = get_long(unit->volume + 28); + uaecptr current = get_long (unit->volume + 28); uaecptr next = 0; while (current) { - next = get_long(current << 2); + 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)); } lock -= 4; put_long (lock, get_long (unit->locklist)); @@ -1411,19 +1397,19 @@ action_lock (Unit *unit, dpacket packet) uaecptr lock = GET_PCK_ARG1 (packet) << 2; uaecptr name = GET_PCK_ARG2 (packet) << 2; long mode = GET_PCK_ARG3 (packet); - struct a_inode *a; + a_inode *a; uae_u32 err; - if (mode != -2 && mode != -1) { + if (mode != SHARED_LOCK && mode != EXCLUSIVE_LOCK) { TRACE(("Bad mode.\n")); - mode = -2; + mode = SHARED_LOCK; } TRACE(("ACTION_LOCK(0x%lx, \"%s\", %d)\n", lock, bstr (unit, name), mode)); DUMPLOCK(unit, lock); a = find_aino (unit, lock, bstr (unit, name), &err); - if (err == 0 && (a->elock || (mode != -2 && a->shlock > 0))) { + if (err == 0 && (a->elock || (mode != SHARED_LOCK && a->shlock > 0))) { err = ERROR_OBJECT_IN_USE; } /* Lock() doesn't do access checks. */ @@ -1432,7 +1418,7 @@ action_lock (Unit *unit, dpacket packet) PUT_PCK_RES2 (packet, err); return; } - if (mode == -2) + if (mode == SHARED_LOCK) a->shlock++; else a->elock = 1; @@ -1443,14 +1429,14 @@ action_lock (Unit *unit, dpacket packet) static void action_free_lock (Unit *unit, dpacket packet) { uaecptr lock = GET_PCK_ARG1 (packet) << 2; - struct a_inode *a; + a_inode *a; TRACE(("ACTION_FREE_LOCK(0x%lx)\n", 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); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND); return; } if (a->elock) @@ -1467,7 +1453,7 @@ static void action_dup_lock (Unit *unit, dpacket packet) { uaecptr lock = GET_PCK_ARG1 (packet) << 2; - struct a_inode *a; + a_inode *a; TRACE(("ACTION_DUP_LOCK(0x%lx)\n", lock)); DUMPLOCK(unit, lock); @@ -1478,7 +1464,7 @@ action_dup_lock (Unit *unit, dpacket pac 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); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND); return; } /* DupLock()ing exclusive locks isn't possible, says the Autodoc, but @@ -1534,8 +1520,23 @@ static void free_exkey (ExamineKey *ek) closedir (ek->dir); } +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; +} + /* This is so sick... who invented ACTION_EXAMINE_NEXT? What did he THINK??? */ -static ExamineKey *new_exkey (Unit *unit, struct a_inode *aino) +static ExamineKey *new_exkey (Unit *unit, a_inode *aino) { uae_u32 uniq; uae_u32 oldest = 0xFFFFFFFF; @@ -1575,23 +1576,8 @@ static ExamineKey *new_exkey (Unit *unit 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, dpacket packet, uaecptr info, struct a_inode *aino) +get_fileinfo (Unit *unit, dpacket packet, uaecptr info, a_inode *aino) { struct stat statbuf; long days, mins, ticks; @@ -1607,37 +1593,46 @@ get_fileinfo (Unit *unit, dpacket packet put_long (info + 120, 1); } else { /* 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 = strrchr (aino->aname, '/'); - if (x) - x++; - else - x = aino->aname; + 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); + 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_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); + 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 */ + 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); } @@ -1645,7 +1640,7 @@ static void do_examine (Unit *unit, dpac { struct dirent de_space; struct dirent* de; - struct a_inode *aino; + a_inode *aino; uae_u32 err; if (!ek->dir) { @@ -1658,13 +1653,9 @@ static void do_examine (Unit *unit, dpac return; } - de = my_readdir (ek->dir, &de_space); - - while (de && (0 == strcmp(".", de->d_name) - || 0 == strcmp("..", de->d_name))) - { + do { de = my_readdir (ek->dir, &de_space); - } + } while (de && fsdb_name_invalid (de->d_name)); if (!de) { TRACE(("no more entries\n")); @@ -1690,28 +1681,28 @@ static void action_examine_object (Unit { uaecptr lock = GET_PCK_ARG1 (packet) << 2; uaecptr info = GET_PCK_ARG2 (packet) << 2; - struct a_inode *aino = 0; + a_inode *aino = 0; TRACE(("ACTION_EXAMINE_OBJECT(0x%lx,0x%lx)\n", lock, info)); DUMPLOCK(unit, lock); if (lock != 0) - aino = lookup_aino (unit, get_long(lock + 4)); + 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); + put_long (info, 0xFFFFFFFF); } else - put_long(info, 0); + put_long (info, 0); } static void action_examine_next (Unit *unit, dpacket packet) { uaecptr lock = GET_PCK_ARG1 (packet) << 2; uaecptr info = GET_PCK_ARG2 (packet) << 2; - struct a_inode *aino = 0; + a_inode *aino = 0; ExamineKey *ek; uae_u32 uniq; @@ -1719,7 +1710,7 @@ static void action_examine_next (Unit *u DUMPLOCK(unit, lock); if (lock != 0) - aino = lookup_aino (unit, get_long(lock + 4)); + aino = lookup_aino (unit, get_long (lock + 4)); if (aino == 0) aino = &unit->rootnode; @@ -1732,15 +1723,15 @@ static void action_examine_next (Unit *u } else if (uniq == 0xFFFFFFFF) { ek = new_exkey(unit, aino); } else - ek = lookup_exkey (unit, get_long(info)); + 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; } - put_long(info, ek->uniq); - do_examine(unit, packet, ek, info); + put_long (info, ek->uniq); + do_examine (unit, packet, ek, info); } static void do_find (Unit *unit, dpacket packet, int mode, int create, int fallback) @@ -1748,7 +1739,7 @@ static void do_find (Unit *unit, dpacket uaecptr fh = GET_PCK_ARG1 (packet) << 2; uaecptr lock = GET_PCK_ARG2 (packet) << 2; uaecptr name = GET_PCK_ARG3 (packet) << 2; - struct a_inode *aino; + a_inode *aino; Key *k; int fd; uae_u32 err; @@ -1760,7 +1751,7 @@ static void do_find (Unit *unit, dpacket aino = find_aino (unit, lock, bstr (unit, name), &err); - if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_FOUND)) { + if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_AROUND)) { /* Whatever it is, we can't handle it. */ PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, err); @@ -1815,7 +1806,7 @@ static void do_find (Unit *unit, dpacket return; } else { /* Object does not exist. aino points to containing directory. */ - aino = new_child_aino (unit, aino, my_strdup (bstr_cut (unit, name)), 1); + 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 */ @@ -1838,7 +1829,7 @@ static void do_find (Unit *unit, dpacket if (aino_created) delete_aino (unit, aino); PUT_PCK_RES1 (packet, DOS_FALSE); - PUT_PCK_RES2 (packet, dos_errno()); + PUT_PCK_RES2 (packet, dos_errno ()); return; } k = new_key (unit); @@ -1859,7 +1850,7 @@ action_fh_from_lock (Unit *unit, dpacket { uaecptr fh = GET_PCK_ARG1 (packet) << 2; uaecptr lock = GET_PCK_ARG2 (packet) << 2; - struct a_inode *aino; + a_inode *aino; Key *k; int fd; mode_t openmode; @@ -1869,14 +1860,14 @@ action_fh_from_lock (Unit *unit, dpacket DUMPLOCK(unit,lock); if (!lock) { - PUT_PCK_RES1 (packet, DOS_FALSE); - PUT_PCK_RES2 (packet, 0); - return; + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, 0); + return; } aino = lookup_aino (unit, get_long (lock + 4)); if (aino == 0) - aino = &unit->rootnode; + aino = &unit->rootnode; mode = aino->amigaos_mode; /* Use same mode for opened filehandle as existing Lock() */ prepare_for_open (aino->nname); @@ -1885,6 +1876,10 @@ action_fh_from_lock (Unit *unit, dpacket : (mode & A_FIBF_WRITE) == 0 ? O_RDONLY : O_RDWR)); + /* the files on CD really can have the write-bit set. */ + if (unit->ui.readonly) + openmode = O_RDONLY; + fd = open (aino->nname, openmode | O_BINARY, 0777); if (fd < 0) { @@ -2102,7 +2097,7 @@ action_set_protect (Unit *unit, dpacket uaecptr lock = GET_PCK_ARG2 (packet) << 2; uaecptr name = GET_PCK_ARG3 (packet) << 2; uae_u32 mask = GET_PCK_ARG4 (packet); - struct a_inode *a; + a_inode *a; uae_u32 err; TRACE(("ACTION_SET_PROTECT(0x%lx,\"%s\",0x%lx)\n", lock, bstr (unit, name), mask)); @@ -2120,7 +2115,7 @@ action_set_protect (Unit *unit, dpacket return; } - err = set_file_attrs (a, mask); + err = fsdb_set_file_attrs (a, mask); if (err != 0) { PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, err); @@ -2129,6 +2124,48 @@ action_set_protect (Unit *unit, dpacket } } +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 (unit->ui.readonly) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED); + return; + } + + commented = bstr (unit, comment); + commented = strlen (commented) > 0 ? my_strdup (commented) : 0; + TRACE (("ACTION_SET_COMMENT(0x%lx,\"%s\")\n", lock, commented)); + + a = find_aino (unit, lock, bstr (unit, name), &err); + if (err != 0) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + + maybe_free_and_out: + if (commented) + free (commented); + return; + } + 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, dpacket packet) { @@ -2157,25 +2194,37 @@ action_change_mode (Unit *unit, dpacket /* 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; - struct a_inode *a = NULL, *olda = NULL; + unsigned long uniq; + 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; + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_INVALID_LOCK); + return; } /* @@@ 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 (type == CHANGE_LOCK) + uniq = get_long (object + 4); + else { + Key *k = lookup_key (unit, object); + if (!k) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND); + return; + } + uniq = k->aino->uniq; + } + a = lookup_aino (unit, uniq); + if (! a) err = ERROR_INVALID_LOCK; else { @@ -2190,22 +2239,22 @@ action_change_mode (Unit *unit, dpacket a->elock = 0; a->shlock++; } - } + } if (err) { - PUT_PCK_RES1 (packet, DOS_FALSE); - PUT_PCK_RES2 (packet, err); - return; + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, err); + return; } else { - de_recycle_aino (unit, a); - PUT_PCK_RES1 (packet, DOS_TRUE); + de_recycle_aino (unit, a); + PUT_PCK_RES1 (packet, DOS_TRUE); } } static void -action_parent_common (Unit *unit, dpacket packet, uaecptr fh) +action_parent_common (Unit *unit, dpacket packet, unsigned long uniq) { - struct a_inode *olda = lookup_aino (unit, fh); + a_inode *olda = lookup_aino (unit, uniq); if (olda == 0) { PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, ERROR_INVALID_LOCK); @@ -2230,15 +2279,13 @@ action_parent_common (Unit *unit, dpacke static void action_parent_fh (Unit *unit, dpacket packet) { - uaecptr fh = GET_PCK_ARG1 (packet) << 2; - TRACE(("ACTION_PARENT_FH(0x%lx)\n",fh)); - - if (! fh) { - PUT_PCK_RES1 (packet, DOS_FALSE); - PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_FOUND); - return; + Key *k = lookup_key (unit, GET_PCK_ARG1 (packet)); + if (!k) { + PUT_PCK_RES1 (packet, DOS_FALSE); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND); + return; } - action_parent_common (unit, packet, fh); + action_parent_common (unit, packet, k->aino->uniq); } static void @@ -2261,7 +2308,7 @@ action_create_dir (Unit *unit, dpacket p { uaecptr lock = GET_PCK_ARG1 (packet) << 2; uaecptr name = GET_PCK_ARG2 (packet) << 2; - struct a_inode *aino; + a_inode *aino; uae_u32 err; TRACE(("ACTION_CREATE_DIR(0x%lx,\"%s\")\n", lock, bstr (unit, name))); @@ -2273,7 +2320,7 @@ action_create_dir (Unit *unit, dpacket p } aino = find_aino (unit, lock, bstr (unit, name), &err); - if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_FOUND)) { + if (aino == 0 || (err != 0 && err != ERROR_OBJECT_NOT_AROUND)) { PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, err); return; @@ -2285,7 +2332,7 @@ action_create_dir (Unit *unit, dpacket p return; } /* Object does not exist. aino points to containing directory. */ - aino = new_child_aino (unit, aino, my_strdup (bstr_cut (unit, name)), 2); + 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 */ @@ -2306,7 +2353,7 @@ static void action_examine_fh (Unit *unit, dpacket packet) { Key *k; - struct a_inode *aino = 0; + a_inode *aino = 0; uaecptr info = GET_PCK_ARG2 (packet) << 2; TRACE(("ACTION_EXAMINE_FH(0x%lx,0x%lx)\n", @@ -2336,7 +2383,7 @@ action_set_file_size (Unit *unit, dpacke 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; @@ -2345,7 +2392,7 @@ action_set_file_size (Unit *unit, dpacke 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); + PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND); return; } @@ -2384,7 +2431,7 @@ action_delete_object (Unit *unit, dpacke { uaecptr lock = GET_PCK_ARG1 (packet) << 2; uaecptr name = GET_PCK_ARG2 (packet) << 2; - struct a_inode *a; + a_inode *a; uae_u32 err; TRACE(("ACTION_DELETE_OBJECT(0x%lx,\"%s\")\n", lock, bstr (unit, name))); @@ -2402,12 +2449,19 @@ action_delete_object (Unit *unit, dpacke 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) { + /* This should take care of removing the fsdb if no files remain. */ + fsdb_dir_writeback (a); if (rmdir (a->nname) == -1) { PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, dos_errno()); @@ -2434,7 +2488,7 @@ action_set_date (Unit *unit, dpacket pac uaecptr lock = GET_PCK_ARG2 (packet) << 2; uaecptr name = GET_PCK_ARG3 (packet) << 2; uaecptr date = GET_PCK_ARG4 (packet); - struct a_inode *a; + a_inode *a; struct utimbuf ut; uae_u32 err; @@ -2465,7 +2519,7 @@ action_rename_object (Unit *unit, dpacke uaecptr name1 = GET_PCK_ARG2 (packet) << 2; uaecptr lock2 = GET_PCK_ARG3 (packet) << 2; uaecptr name2 = GET_PCK_ARG4 (packet) << 2; - struct a_inode *a1, *a2; + a_inode *a1, *a2; uae_u32 err1, err2; TRACE(("ACTION_RENAME_OBJECT(0x%lx,\"%s\",", lock1, bstr (unit, name1))); @@ -2483,15 +2537,23 @@ action_rename_object (Unit *unit, dpacke 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 == 0 || err2 != ERROR_OBJECT_NOT_FOUND) { + 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_AROUND) { PUT_PCK_RES1 (packet, DOS_FALSE); PUT_PCK_RES2 (packet, err2 == 0 ? ERROR_OBJECT_EXISTS : err2); return; } - /* Object does not exist. aino points to containing directory. */ - a2 = new_child_aino (unit, a2, my_strdup (bstr_cut (unit, name2)), - a1->dir ? 2 : 1); + + 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 */ @@ -2504,6 +2566,9 @@ action_rename_object (Unit *unit, dpacke PUT_PCK_RES2 (packet, dos_errno ()); return; } + 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); @@ -2565,6 +2630,7 @@ 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)) { @@ -2590,6 +2656,7 @@ static uae_u32 exter_int_helper (void) * 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; @@ -2603,8 +2670,42 @@ static uae_u32 exter_int_helper (void) 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; + + default: + write_log ("exter_int_helper: unknown native action\n"); + break; + } + } +#endif + /* 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 @@ -2612,6 +2713,7 @@ static uae_u32 exter_int_helper (void) 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) @@ -2619,13 +2721,12 @@ static uae_u32 exter_int_helper (void) unit_no++; } uip[unit_no].self->cmds_acked = uip[unit_no].self->cmds_sent; - return uip[unit_no++].self->port; - - case 3: - /* Look up a message for a port. */ - { - Unit *unit = find_unit (m68k_areg (regs, 2)); - return unit->dummy_message; + 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 4: @@ -2660,6 +2761,7 @@ static int handle_packet (Unit *unit, dp 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; @@ -2696,7 +2798,7 @@ static int handle_packet (Unit *unit, dp } #ifdef UAE_FILESYS_THREADS -static void *filesys_penguin (void *unit_v) +static void *filesys_thread (void *unit_v) { UnitInfo *ui = (UnitInfo *)unit_v; for (;;) { @@ -2734,7 +2836,7 @@ static void *filesys_penguin (void *unit 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); + write_comm_pipe_int (ui->back_pipe, (int)(get_long (ui->self->locklist)), 0); put_long (ui->self->locklist, 0); } return 0; @@ -2797,6 +2899,32 @@ static uae_u32 filesys_handler (void) return 0; } +static int current_deviceno = 0; +static int current_cdrom = 0; + +static void reset_uaedevices (void) +{ + current_deviceno = 0; + current_cdrom = 0; +} + +static int get_new_device (char **devname, uaecptr *devname_amiga, int cdrom) +{ + int result; + char buffer[80]; + + if (cdrom) { + sprintf (buffer, "CD%d", current_cdrom); + result = current_cdrom++; + } else { + sprintf (buffer, "DH%d", current_deviceno); + result = current_deviceno++; + } + + *devname_amiga = ds (*devname = my_strdup (buffer)); + return result; +} + void filesys_start_threads (void) { UnitInfo *uip; @@ -2809,7 +2937,7 @@ void filesys_start_threads (void) 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); + uip[i].devno = get_new_device (&uip[i].devname, &uip[i].devname_amiga, 0); #ifdef UAE_FILESYS_THREADS if (! is_hardfile (current_mountinfo, i)) { @@ -2817,7 +2945,7 @@ void filesys_start_threads (void) 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); + uae_start_thread (filesys_thread, (void *)(uip + i), &uip[i].tid); } #endif } @@ -2826,7 +2954,6 @@ void filesys_start_threads (void) void filesys_reset (void) { Unit *u, *u1; - int i; /* We get called once from customreset at the beginning of the program * before filesys_start_threads has been called. Survive that. */ @@ -2866,11 +2993,11 @@ void filesys_prepare_reset (void) u = units; while (u != 0) { while (u->rootnode.next != &u->rootnode) { - struct a_inode *a = u->rootnode.next; + a_inode *b; + a_inode *a = u->rootnode.next; u->rootnode.next = a->next; - free (a->nname); - free (a->aname); - free (a); + de_recycle_aino (u, a); + dispose_aino (u, &b, a); } u = u->next; } @@ -2878,11 +3005,13 @@ void filesys_prepare_reset (void) static uae_u32 filesys_diagentry (void) { - uaecptr resaddr = m68k_areg(regs, 2) + 0x10; + uaecptr resaddr = m68k_areg (regs, 2) + 0x10; + uaecptr start = resaddr; + uaecptr residents, tmp; TRACE (("filesystem: diagentry called\n")); - filesys_configdev = m68k_areg(regs, 3); + filesys_configdev = m68k_areg (regs, 3); do_put_mem_long ((uae_u32 *)(filesysory + 0x2100), EXPANSION_explibname); do_put_mem_long ((uae_u32 *)(filesysory + 0x2104), filesys_configdev); @@ -2893,21 +3022,48 @@ static uae_u32 filesys_diagentry (void) 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 */ + 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. */ + * 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++; + } + } + put_word (resaddr, 0x7001); /* moveq.l #1,d0 */ + put_word (resaddr + 2, RTS); + m68k_areg (regs, 0) = residents; return 1; } @@ -2968,7 +3124,8 @@ void filesys_install (void) fsdevname = ds ("uae.device"); /* does not really exist */ ROM_filesys_diagentry = here(); - calltrap (deftrap(filesys_diagentry)); dw(RTS); + calltrap (deftrap(filesys_diagentry)); + dw(0x4ED0); /* JMP (a0) - jump to code that inits Residents */ loop = here (); /* Special trap for the assembly make_dev routine */ @@ -2992,10 +3149,6 @@ void filesys_install (void) calltrap (deftrap (exter_int_helper)); dw (RTS); - org (0xF0FF70); - calltrap (deftrap (mousehack_helper)); - dw (RTS); - org (loop); } @@ -3004,110 +3157,114 @@ void filesys_install_code (void) align(4); /* The last offset comes from the code itself, look for it near the top. */ - filesys_initcode = here() + 8 + 0x6c; 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(0xc0); db(0x00); db(0x00); db(0x01); db(0x2c); - db(0x00); db(0x00); db(0x00); db(0x6c); db(0x00); db(0x00); db(0x00); db(0x28); - db(0x00); db(0x00); db(0x00); db(0x14); db(0x43); db(0xfa); db(0x02); db(0xf9); + 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(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(0x2e); - 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(0x4a); db(0x80); db(0x67); db(0x14); - db(0x24); db(0x40); db(0x70); db(0x03); db(0x4e); db(0xb9); db(0x00); db(0xf0); - db(0xff); db(0x50); db(0x20); db(0x4a); db(0x22); db(0x40); db(0x4e); db(0xae); - db(0xfe); db(0x92); db(0x60); db(0xe0); 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(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(0x9f); 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(0x8f); 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(0x12); db(0x20); db(0x4b); - db(0x48); db(0xe7); db(0x02); db(0x10); db(0x7e); db(0x01); db(0x61); db(0x6c); - db(0x4c); db(0xdf); db(0x08); db(0x40); db(0x52); db(0x86); db(0x60); db(0xe8); - db(0x2c); db(0x78); db(0x00); db(0x04); db(0x22); db(0x4c); db(0x4e); db(0xae); - db(0xfe); db(0x62); db(0x61); db(0x28); db(0x2c); db(0x78); db(0x00); db(0x04); + 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(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(0xf0); db(0x23); db(0x48); db(0x00); db(0x0a); db(0x41); db(0xfa); - db(0xff); db(0x10); db(0x23); db(0x48); db(0x00); db(0x0e); db(0x41); db(0xfa); - db(0xff); db(0x08); 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(0xbc); 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(0x94); 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(0x39); - 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(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(0x70); db(0x00); db(0x4e); db(0xb9); db(0x00); db(0xf0); - db(0xff); db(0x40); db(0x60); db(0x74); 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(0x40); 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(0xba); db(0xbc); db(0x00); db(0x00); db(0x00); db(0x14); db(0x65); db(0x08); - 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(0x8c); - db(0x28); db(0x43); db(0x61); db(0x02); db(0x60); db(0x86); 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(0x0a); + 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(0xf2); db(0x52); db(0x85); db(0x67); db(0x3c); db(0x24); db(0x47); + 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(0xdc); db(0xbc); db(0x00); db(0x01); db(0x00); db(0x00); db(0x20); db(0x0a); + 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(0x72); db(0x61); db(0x72); db(0x79); db(0x00); db(0x00); db(0x00); db(0x00); + db(0x00); db(0x00); db(0x03); db(0xf2); }