Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: d87399cfde
Fetching contributors…

Cannot retrieve contributors at this time

7474 lines (6738 sloc) 195.493 kb
/*
* UAE - The Un*x Amiga Emulator
*
* Unix file system handler for AmigaDOS
*
* Copyright 1996 Ed Hanway
* Copyright 1996, 1997 Bernd Schmidt
*
* 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.
* (May - August 1989)
*
* Known limitations:
* Does not support several (useless) 2.0+ packet types.
* 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.
* 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 "threaddep/thread.h"
#include "options.h"
#include "uae.h"
#include "memory.h"
#include "custom.h"
#include "events.h"
#include "newcpu.h"
#include "filesys.h"
#include "autoconf.h"
#include "traps.h"
#include "fsusage.h"
#include "native2amiga.h"
#include "scsidev.h"
#include "fsdb.h"
#include "zfile.h"
#include "gui.h"
#include "gayle.h"
#include "savestate.h"
#include "consolehook.h"
#include "blkdev.h"
#include "isofs_api.h"
//FIXME: ---start
#ifdef TARGET_AMIGAOS
#include <dos/dos.h>
#include <proto/dos.h>
#endif
#define my_rmdir rmdir
#define my_unlink unlink
#define my_rename rename
#define my_truncate truncate
#define my_opendir opendir
#define my_closedir closedir
#define my_open fopen
#define my_close fclose
#define my_lseek fseek
//#define my_strdup strdup
#define _stat64 stat
//FIXME: ---end
#define TRACING_ENABLED 0
#if TRACING_ENABLED
#define TRACE(x) do { write_log x; } while(0)
#define DUMPLOCK(u,x) dumplock(u,x)
#if TRACING_ENABLED > 1
#define TRACE2(x) do { write_log x; } while(0)
#else
#define TRACE2(x)
#endif
#else
#define TRACE(x)
#define DUMPLOCK(u,x)
#define TRACE2(x)
#endif
#define RTAREA_HEARTBEAT 0xFFFC
static void get_time (time_t t, long* days, long* mins, long* ticks);
static uae_sem_t test_sem;
int bootrom_header, bootrom_items;
static uae_u32 dlg (uae_u32 a)
{
return (dbg (a + 0) << 24) | (dbg (a + 1) << 16) | (dbg (a + 2) << 8) | (dbg (a + 3) << 0);
}
static void aino_test (a_inode *aino)
{
#ifdef AINO_DEBUG
a_inode *aino2 = aino, *aino3;
for (;;) {
if (!aino || !aino->next)
return;
if ((aino->checksum1 ^ aino->checksum2) != 0xaaaa5555) {
write_log (_T("PANIC: corrupted or freed but used aino detected!"), aino);
}
aino3 = aino;
aino = aino->next;
if (aino->prev != aino3) {
write_log (_T("PANIC: corrupted aino linking!\n"));
break;
}
if (aino == aino2) break;
}
#endif
}
static void aino_test_init (a_inode *aino)
{
#ifdef AINO_DEBUG
aino->checksum1 = (uae_u32)aino;
aino->checksum2 = aino->checksum1 ^ 0xaaaa5555;
#endif
}
uaecptr filesys_initcode;
static uae_u32 fsdevname, fshandlername, filesys_configdev;
static uae_u32 cdfs_devname, cdfs_handlername, cdfs_control;
static int filesys_in_interrupt;
static uae_u32 mountertask;
static int automountunit = -1;
static int cd_unit_offset, cd_unit_number;
#define FS_STARTUP 0
#define FS_GO_DOWN 1
#define DEVNAMES_PER_HDF 32
#define UNIT_FILESYSTEM 0
#define UNIT_CDFS 1
typedef struct {
int unit_type;
bool open;
TCHAR *devname; /* device name, e.g. UAE0: */
uaecptr devname_amiga;
uaecptr startup;
uaecptr devicenode;
TCHAR *volname; /* volume name, e.g. CDROM, WORK, etc. */
int volflags; /* volume flags, readonly, stream uaefsdb support */
TCHAR *rootdir; /* root native directory/hdf. empty drive if invalid path */
struct zvolume *zarchive;
TCHAR *rootdirdiff; /* "diff" file/directory */
bool readonly; /* disallow write access? */
bool locked; /* action write protect */
bool unknown_media; /* ID_UNREADABLE_DISK */
int bootpri; /* boot priority. -128 = no autoboot, -129 = no mount */
int devno;
int controller;
bool wasisempty; /* if true, this unit was created empty */
bool canremove; /* if true, this unit can be safely ejected and remounted */
bool configureddrive; /* if true, this is drive that was manually configured */
struct hardfiledata hf;
/* Threading stuff */
smp_comm_pipe *volatile unit_pipe, *volatile back_pipe;
uae_thread_id tid;
struct _unit *self;
/* Reset handling */
uae_sem_t reset_sync_sem;
volatile int reset_state;
/* RDB stuff */
uaecptr rdb_devname_amiga[DEVNAMES_PER_HDF];
int rdb_lowcyl;
int rdb_highcyl;
int rdb_cylblocks;
uae_u8 *rdb_filesysstore;
int rdb_filesyssize;
TCHAR *filesysdir;
/* CDFS */
bool cd_open;
int cddevno;
void *cdfs_superblock;
} UnitInfo;
struct uaedev_mount_info {
UnitInfo ui[MAX_FILESYSTEM_UNITS];
};
static struct uaedev_mount_info mountinfo;
int nr_units (void)
{
int i, cnt = 0;
for (i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (mountinfo.ui[i].open)
cnt++;
}
return cnt;
}
int nr_directory_units (struct uae_prefs *p)
{
int i, cnt = 0;
if (p) {
for (i = 0; i < p->mountitems; i++) {
if (p->mountconfig[i].controller == 0)
cnt++;
}
} else {
for (i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (mountinfo.ui[i].open && mountinfo.ui[i].controller == 0)
cnt++;
}
}
return cnt;
}
static int is_virtual (int unit_no)
{
int t = is_hardfile (unit_no);
return t == FILESYS_VIRTUAL || t == FILESYS_CD;
}
int is_hardfile (int unit_no)
{
if (mountinfo.ui[unit_no].volname || mountinfo.ui[unit_no].wasisempty || mountinfo.ui[unit_no].unknown_media) {
if (unit_no >= cd_unit_offset && unit_no < cd_unit_offset + cd_unit_number)
return FILESYS_CD;
return FILESYS_VIRTUAL;
}
if (mountinfo.ui[unit_no].hf.secspertrack == 0) {
if (mountinfo.ui[unit_no].hf.flags & 1)
return FILESYS_HARDDRIVE;
return FILESYS_HARDFILE_RDB;
}
return FILESYS_HARDFILE;
}
static void close_filesys_unit (UnitInfo *uip)
{
if (!uip->open)
return;
if (uip->hf.handle_valid)
hdf_close (&uip->hf);
if (uip->volname != 0)
xfree (uip->volname);
if (uip->devname != 0)
xfree (uip->devname);
if (uip->rootdir != 0)
xfree (uip->rootdir);
if (uip->unit_pipe)
xfree (uip->unit_pipe);
if (uip->back_pipe)
xfree (uip->back_pipe);
if (uip->cd_open) {
#ifdef SCSI
sys_command_close (uip->cddevno);
isofs_unmount (uip->cdfs_superblock);
#endif
}
uip->unit_pipe = 0;
uip->back_pipe = 0;
uip->hf.handle_valid = 0;
uip->volname = 0;
uip->devname = 0;
uip->rootdir = 0;
uip->open = 0;
uip->cd_open = 0;
}
static UnitInfo *getuip (struct uae_prefs *p, int index)
{
if (index < 0)
return NULL;
index = p->mountconfig[index].configoffset;
if (index < 0)
return NULL;
return &mountinfo.ui[index];
}
int get_filesys_unitconfig (struct uae_prefs *p, int index, struct mountedinfo *mi)
{
UnitInfo *ui = getuip(p, index);
struct uaedev_config_info *uci = &p->mountconfig[index];
UnitInfo uitmp;
memset (mi, 0, sizeof (struct mountedinfo));
memset (&uitmp, 0, sizeof uitmp);
if (!ui) {
ui = &uitmp;
if (!uci->ishdf) {
mi->ismounted = 1;
if (uci->rootdir && _tcslen(uci->rootdir) == 0)
return FILESYS_VIRTUAL;
if (my_existsfile (uci->rootdir)) {
mi->ismedia = 1;
return FILESYS_VIRTUAL;
}
if (my_getvolumeinfo (uci->rootdir) < 0)
return -1;
mi->ismedia = true;
return FILESYS_VIRTUAL;
} else {
ui->hf.readonly = true;
ui->hf.blocksize = uci->blocksize;
if (!hdf_open (&ui->hf, uci->rootdir)) {
mi->ismedia = false;
mi->ismounted = true;
if (uci->reserved == 0 && uci->sectors == 0 && uci->surfaces == 0) {
if (ui->hf.flags & 1)
return FILESYS_HARDDRIVE;
return FILESYS_HARDFILE_RDB;
}
return -1;
}
mi->ismedia = true;
if (ui->hf.drive_empty)
mi->ismedia = 0;
hdf_close (&ui->hf);
}
} else {
if (!ui->controller || (ui->controller && p->cs_ide)) {
mi->ismounted = 1;
if (uci->ishdf)
mi->ismedia = ui->hf.drive_empty ? false : true;
else
mi->ismedia = true;
}
}
mi->size = ui->hf.virtsize;
mi->nrcyls = (int)(uci->sectors * uci->surfaces ? (ui->hf.virtsize / uci->blocksize) / (uci->sectors * uci->surfaces) : 0);
if (!uci->ishdf)
return FILESYS_VIRTUAL;
if (uci->reserved == 0 && uci->sectors == 0 && uci->surfaces == 0) {
if (ui->hf.flags & 1)
return FILESYS_HARDDRIVE;
return FILESYS_HARDFILE_RDB;
}
return FILESYS_HARDFILE;
}
static void stripsemicolon (TCHAR *s)
{
if (!s)
return;
while (_tcslen(s) > 0 && s[_tcslen(s) - 1] == ':')
s[_tcslen(s) - 1] = 0;
}
static void stripspace (TCHAR *s)
{
int i;
if (!s)
return;
for (i = 0; i < _tcslen (s); i++) {
if (s[i] == ' ')
s[i] = '_';
}
}
static void striplength (TCHAR *s, int len)
{
if (!s)
return;
if (_tcslen (s) <= len)
return;
s[len] = 0;
}
static void fixcharset (TCHAR *s)
{
char tmp[MAX_DPATH];
if (!s)
return;
//ua_fs_copy (tmp, MAX_DPATH, s, '_');
strcpy (tmp, s);
au_fs_copy (s, strlen (tmp) + 1, tmp);
}
TCHAR *validatevolumename (TCHAR *s)
{
stripsemicolon (s);
fixcharset (s);
striplength (s, 30);
return s;
}
TCHAR *validatedevicename (TCHAR *s)
{
stripsemicolon (s);
stripspace (s);
fixcharset (s);
striplength (s, 30);
return s;
}
TCHAR *filesys_createvolname (const TCHAR *volname, const TCHAR *rootdir, const TCHAR *def)
{
TCHAR *nvol = NULL;
int i, archivehd;
TCHAR *p = NULL;
archivehd = -1;
if (my_existsfile (rootdir))
archivehd = 1;
else if (my_existsdir (rootdir))
archivehd = 0;
if ((!volname || _tcslen (volname) == 0) && rootdir && archivehd >= 0) {
p = my_strdup (rootdir);
for (i = _tcslen (p) - 1; i >= 0; i--) {
TCHAR c = p[i];
if (c == ':' || c == '/' || c == '\\') {
if (i == _tcslen (p) - 1)
continue;
if (!_tcscmp (p + i, _T(":\\"))) {
xfree (p);
p = xmalloc (TCHAR, 10);
p[0] = rootdir[0];
p[1] = 0;
i = 0;
} else {
i++;
}
break;
}
}
if (i >= 0)
nvol = my_strdup (p + i);
}
if (!nvol && archivehd >= 0) {
TCHAR *s = NULL;
if (volname && _tcslen (volname) > 0)
nvol = my_strdup (volname);
else
nvol = my_strdup (def);
}
if (!nvol) {
if (volname && _tcslen (volname))
nvol = my_strdup (volname);
else
nvol = my_strdup (_T(""));
}
validatevolumename (nvol);
xfree (p);
return nvol;
}
static int set_filesys_volume (const TCHAR *rootdir, int *flags, bool *readonly, bool *emptydrive, struct zvolume **zvp)
{
*emptydrive = 0;
//FIXME: we dont support.. yet.. -mustafa
// if (my_existsfile (rootdir)) {
if (0) {
struct zvolume *zv;
zv = 1; //zfile_fopen_archive (rootdir);
if (!zv) {
write_log (_T("'%s' is not a supported archive file\n"), rootdir);
return -1;
}
*zvp = zv;
*flags = MYVOLUMEINFO_ARCHIVE;
*readonly = 1;
} else {
*flags = my_getvolumeinfo (rootdir);
if (*flags < 0) {
if (rootdir && rootdir[0])
write_log (_T("directory '%s' not found, mounting as empty drive\n"), rootdir);
*emptydrive = 1;
*flags = 0;
} else if ((*flags) & MYVOLUMEINFO_READONLY) {
write_log (_T("'%s' set to read-only\n"), rootdir);
*readonly = 1;
}
}
return 1;
}
static int set_filesys_unit_1 (int nr,
TCHAR *devname, TCHAR *volname, const TCHAR *rootdir, bool readonly,
int secspertrack, int surfaces, int reserved,
int blocksize, int bootpri, bool donotmount, bool autoboot,
TCHAR *filesysdir, int hdc, int flags)
{
UnitInfo *ui;
int i;
bool emptydrive = false;
bool iscd;
if (hdc)
return -1;
if (nr < 0) {
for (nr = 0; nr < MAX_FILESYSTEM_UNITS; nr++) {
if (!mountinfo.ui[nr].open)
break;
}
if (nr == MAX_FILESYSTEM_UNITS) {
write_log (_T("No slot allocated for this unit\n"));
return -1;
}
}
iscd = USE_CDFS == 2 && nr >= cd_unit_offset && nr < cd_unit_offset + cd_unit_number;
for (i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (nr == i || !mountinfo.ui[i].open || mountinfo.ui[i].rootdir == NULL || is_hardfile (i) == FILESYS_CD)
continue;
if (rootdir && _tcslen (rootdir) > 0 && !_tcsicmp (mountinfo.ui[i].rootdir, rootdir)) {
write_log (_T("directory/hardfile '%s' already added\n"), rootdir);
return -1;
}
}
ui = &mountinfo.ui[nr];
memset (ui, 0, sizeof (UnitInfo));
if (iscd) {
ui->unit_type = UNIT_CDFS;
emptydrive = 1;
ui->volflags = MYVOLUMEINFO_CDFS | MYVOLUMEINFO_READONLY;
readonly = true;
} else if (volname != NULL) {
int flags = 0;
emptydrive = 1;
if (rootdir) {
if (set_filesys_volume (rootdir, &flags, &readonly, &emptydrive, &ui->zarchive) < 0)
return -1;
}
ui->volname = filesys_createvolname (volname, rootdir, _T("harddrive"));
ui->volflags = flags;
} else {
ui->unit_type = UNIT_FILESYSTEM;
ui->hf.secspertrack = secspertrack;
ui->hf.surfaces = surfaces;
ui->hf.reservedblocks = reserved;
ui->hf.blocksize = blocksize;
ui->hf.unitnum = nr;
ui->volname = 0;
ui->hf.readonly = readonly;
if (!hdf_open (&ui->hf, rootdir) && !readonly) {
write_log (_T("Attempting to open in read-only mode\n"));
ui->hf.readonly = readonly = 1;
hdf_open (&ui->hf, rootdir);
}
ui->hf.readonly = readonly;
if (!ui->hf.drive_empty) {
if (ui->hf.handle_valid == 0) {
write_log (_T("Hardfile %s not found\n"), ui->hf.device_name);
goto err;
}
if ((ui->hf.blocksize & (ui->hf.blocksize - 1)) != 0 || ui->hf.blocksize == 0) {
write_log (_T("Hardfile %s bad blocksize\n"), ui->hf.device_name);
goto err;
}
if ((ui->hf.secspertrack || ui->hf.surfaces || ui->hf.reservedblocks) &&
(ui->hf.secspertrack < 1 || ui->hf.surfaces < 1 || ui->hf.surfaces > 1023 ||
ui->hf.reservedblocks < 0 || ui->hf.reservedblocks > 1023) != 0) {
write_log (_T("Hardfile %s bad hardfile geometry\n"), ui->hf.device_name);
goto err;
}
if (ui->hf.blocksize > ui->hf.virtsize || ui->hf.virtsize == 0) {
write_log (_T("Hardfile %s too small\n"), ui->hf.device_name);
goto err;
}
ui->hf.nrcyls = (int)(ui->hf.secspertrack * ui->hf.surfaces ? (ui->hf.virtsize / ui->hf.blocksize) / (ui->hf.secspertrack * ui->hf.surfaces) : 0);
}
}
ui->self = 0;
ui->reset_state = FS_STARTUP;
ui->wasisempty = emptydrive;
ui->canremove = emptydrive && (flags & MYVOLUMEINFO_REUSABLE);
ui->rootdir = my_strdup (rootdir);
ui->devname = my_strdup (devname);
stripsemicolon(ui->devname);
if (filesysdir && filesysdir[0])
ui->filesysdir = my_strdup (filesysdir);
ui->readonly = readonly;
if (!autoboot)
bootpri = -128;
if (donotmount)
bootpri = -129;
if (bootpri < -129)
bootpri = -129;
if (bootpri > 127)
bootpri = 127;
ui->bootpri = bootpri;
ui->open = 1;
return nr;
err:
if (ui->hf.handle_valid)
hdf_close (&ui->hf);
return -1;
}
int set_filesys_unit (int nr,
TCHAR *devname, TCHAR *volname, const TCHAR *rootdir, bool readonly,
int secspertrack, int surfaces, int reserved,
int blocksize, int bootpri, bool donotmount, bool autoboot,
TCHAR *filesysdir, int hdc, int flags)
{
int ret;
ret = set_filesys_unit_1 (nr, devname, volname, rootdir, readonly,
secspertrack, surfaces, reserved, blocksize, bootpri, donotmount, autoboot,
filesysdir, hdc, flags);
return ret;
}
int add_filesys_unit (TCHAR *devname, TCHAR *volname, const TCHAR *rootdir, bool readonly,
int secspertrack, int surfaces, int reserved,
int blocksize, int bootpri, bool donotmount, bool autoboot,
TCHAR *filesysdir, int hdc, int flags)
{
int ret;
if (nr_units() >= MAX_FILESYSTEM_UNITS)
return -1;
ret = set_filesys_unit_1 (-1, devname, volname, rootdir, readonly,
secspertrack, surfaces, reserved, blocksize,
bootpri, donotmount, autoboot, filesysdir, hdc, flags);
#ifdef RETROPLATFORM
if (ret >= 0) {
rp_hd_device_enable (ret, true);
rp_harddrive_image_change (ret, readonly, rootdir);
}
#endif
return ret;
}
int kill_filesys_unitconfig (struct uae_prefs *p, int nr)
{
struct uaedev_config_info *uci;
if (nr < 0)
return 0;
uci = &p->mountconfig[nr];
hardfile_do_disk_change (uci, 0);
if (uci->configoffset >= 0 && uci->controller == 0)
filesys_media_change (uci->rootdir, 0, uci);
while (nr < MOUNT_CONFIG_SIZE) {
memmove (&p->mountconfig[nr], &p->mountconfig[nr + 1], sizeof (struct uaedev_config_info));
nr++;
}
p->mountitems--;
memset (&p->mountconfig[MOUNT_CONFIG_SIZE - 1], 0, sizeof (struct uaedev_config_info));
return 1;
}
int move_filesys_unitconfig (struct uae_prefs *p, int nr, int to)
{
struct uaedev_config_info *uci1, *uci2, tmpuci;
uci1 = &p->mountconfig[nr];
uci2 = &p->mountconfig[to];
if (nr == to)
return 0;
memcpy (&tmpuci, uci1, sizeof (struct uaedev_config_info));
memcpy (uci1, uci2, sizeof (struct uaedev_config_info));
memcpy (uci2, &tmpuci, sizeof (struct uaedev_config_info));
return 1;
}
static void filesys_addexternals (void);
static void initialize_mountinfo (void)
{
int nr;
struct uaedev_config_info *uci;
UnitInfo *uip = &mountinfo.ui[0];
cd_unit_offset = MAX_FILESYSTEM_UNITS;
for (nr = 0; nr < currprefs.mountitems; nr++) {
uci = &currprefs.mountconfig[nr];
if (uci->controller == HD_CONTROLLER_UAE) {
int idx = set_filesys_unit_1 (-1, uci->devname, uci->ishdf ? NULL : uci->volname, uci->rootdir,
uci->readonly, uci->sectors, uci->surfaces, uci->reserved,
uci->blocksize, uci->bootpri, uci->donotmount, uci->autoboot, uci->filesys, 0, MYVOLUMEINFO_REUSABLE);
if (idx >= 0) {
UnitInfo *ui;
uci->configoffset = idx;
ui = &mountinfo.ui[idx];
ui->configureddrive = 1;
}
} else if (uci->controller <= HD_CONTROLLER_IDE3) {
#ifdef GAYLE
gayle_add_ide_unit (uci->controller - HD_CONTROLLER_IDE0, uci->rootdir, uci->blocksize, uci->readonly,
uci->devname, uci->sectors, uci->surfaces, uci->reserved,
uci->bootpri, uci->filesys);
#endif
} else if (uci->controller <= HD_CONTROLLER_SCSI6) {
if (currprefs.cs_mbdmac) {
#ifdef A2091
a3000_add_scsi_unit (uci->controller - HD_CONTROLLER_SCSI0, uci->rootdir, uci->blocksize, uci->readonly,
uci->devname, uci->sectors, uci->surfaces, uci->reserved,
uci->bootpri, uci->filesys);
#endif
} else if (currprefs.cs_a2091) {
#ifdef A2091
a2091_add_scsi_unit (uci->controller - HD_CONTROLLER_SCSI0, uci->rootdir, uci->blocksize, uci->readonly,
uci->devname, uci->sectors, uci->surfaces, uci->reserved,
uci->bootpri, uci->filesys);
#endif
} else if (currprefs.cs_cdtvscsi) {
#ifdef CDTV
cdtv_add_scsi_unit (uci->controller - HD_CONTROLLER_SCSI0, uci->rootdir, uci->blocksize, uci->readonly,
uci->devname, uci->sectors, uci->surfaces, uci->reserved,
uci->bootpri, uci->filesys);
#endif
}
} else if (uci->controller == HD_CONTROLLER_PCMCIA_SRAM) {
#ifdef GAYLE
gayle_add_pcmcia_sram_unit (uci->rootdir, uci->readonly);
} else if (uci->controller == HD_CONTROLLER_PCMCIA_IDE) {
gayle_add_pcmcia_ide_unit (uci->rootdir, uci->readonly);
}
#endif
}
//filesys_addexternals ();
cd_unit_offset = nr_units ();
cd_unit_number = 0;
#ifdef SCSI
#if USE_CDFS == 2
if (currprefs.scsi /*&& currprefs.win32_automount_cddrives*/ && USE_CDFS) {
uae_u32 mask = scsi_get_cd_drive_mask ();
for (unsigned int i = 0; i < 32; i++) {
if (mask & (1 << i)) {
TCHAR cdname[30];
_stprintf (cdname, _T("CD%d"), i);
cd_unit_number++;
int idx = set_filesys_unit_1 (i + cd_unit_offset, cdname, NULL, _T("/"), true, 1, 1, 0, 2048, 0, false, false, NULL, 0, 0);
if (idx >= 0) {
UnitInfo *ui;
uci = &currprefs.mountconfig[nr];
uci->configoffset = idx;
ui = &mountinfo.ui[idx];
ui->configureddrive = 1;
nr++;
}
}
}
}
#endif
#endif
}
int sprintf_filesys_unit (TCHAR *buffer, int num)
{
UnitInfo *uip = mountinfo.ui;
if (uip[num].volname != 0)
_stprintf (buffer, _T("(DH%d:) Filesystem, %s: %s %s"), num, uip[num].volname,
uip[num].rootdir, uip[num].readonly ? "ro" : "");
else
_stprintf (buffer, _T("(DH%d:) Hardfile, \"%s\", size %d Mbytes"), num,
uip[num].rootdir, (int)(uip[num].hf.virtsize / (1024 * 1024)));
return 0;
}
void free_mountinfo (void)
{
int i;
for (i = 0; i < MAX_FILESYSTEM_UNITS; i++)
close_filesys_unit (mountinfo.ui + i);
#ifdef GAYLE
gayle_free_units ();
#endif
}
struct hardfiledata *get_hardfile_data (int nr)
{
UnitInfo *uip = mountinfo.ui;
if (nr < 0 || nr >= MAX_FILESYSTEM_UNITS || uip[nr].open == 0 || is_virtual (nr))
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_Arg5 36
#define DP64_INIT -3L
#define dp64_Type 8
#define dp64_Res0 12
#define dp64_Res2 16
#define dp64_Res1 24
#define dp64_Arg1 32
#define dp64_Arg2 40
#define dp64_Arg3 48
#define dp64_Arg4 52
#define dp64_Arg5 56
/* result codes */
#define DOS_TRUE ((uae_u32)-1L)
#define DOS_FALSE (0L)
#define MAXFILESIZE32 (0x7fffffff)
/* 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
#define ACTION_RENAME_DISK 9
#define ACTION_FREE_LOCK 15
#define ACTION_DELETE_OBJECT 16
#define ACTION_RENAME_OBJECT 17
#define ACTION_MORE_CACHE 18
#define ACTION_COPY_DIR 19
#define ACTION_SET_PROTECT 21
#define ACTION_CREATE_DIR 22
#define ACTION_EXAMINE_OBJECT 23
#define ACTION_EXAMINE_NEXT 24
#define ACTION_DISK_INFO 25
#define ACTION_INFO 26
#define ACTION_FLUSH 27
#define ACTION_SET_COMMENT 28
#define ACTION_PARENT 29
#define ACTION_SET_DATE 34
#define ACTION_FIND_WRITE 1004
#define ACTION_FIND_INPUT 1005
#define ACTION_FIND_OUTPUT 1006
#define ACTION_END 1007
#define ACTION_SEEK 1008
#define ACTION_WRITE_PROTECT 1023
#define ACTION_IS_FILESYSTEM 1027
#define ACTION_READ 'R'
#define ACTION_WRITE 'W'
/* 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_ALL 1033
#define ACTION_EXAMINE_FH 1034
#define ACTION_EXAMINE_ALL_END 1035
#define ACTION_FORMAT 1020
#define ACTION_IS_FILESYSTEM 1027
#define ACTION_ADD_NOTIFY 4097
#define ACTION_REMOVE_NOTIFY 4098
#define ACTION_CHANGE_FILE_POSITION64 8001
#define ACTION_GET_FILE_POSITION64 8002
#define ACTION_CHANGE_FILE_SIZE64 8003
#define ACTION_GET_FILE_SIZE64 8004
/* not supported */
#define ACTION_MAKE_LINK 1021
#define ACTION_READ_LINK 1024
#define DISK_TYPE_DOS 0x444f5300 /* DOS\0 */
#define DISK_TYPE_DOS_FFS 0x444f5301 /* DOS\1 */
#define CDFS_DOSTYPE 0x43440000 /* CDxx */
//#define CDFS_DOSTYPE (USE_CDFS == 2 ? 0x43444653 : 0x43445644)
typedef struct {
uae_u32 uniq;
/* The directory we're going through. */
a_inode *aino;
/* The file we're going to look up next. */
a_inode *curr_file;
} ExamineKey;
struct lockrecord
{
struct lockrecord *next;
uae_u32 packet;
uae_u32 pos;
uae_u32 len;
uae_u32 mode;
uae_u32 timeout;
uae_u32 msg;
};
typedef struct key {
struct key *next;
a_inode *aino;
uae_u32 uniq;
struct fs_filehandle *fd;
uae_u64 file_pos;
int dosmode;
int createmode;
int notifyactive;
struct lockrecord *record;
} Key;
typedef struct notify {
struct notify *next;
uaecptr notifyrequest;
TCHAR *fullname;
TCHAR *partname;
} Notify;
typedef struct exallkey {
uae_u32 id;
struct fs_dirhandle *dirhandle;
TCHAR *fn;
uaecptr control;
} ExAllKey;
/* Since ACTION_EXAMINE_NEXT is so braindamaged, we have to keep
* some of these around
*/
#define EXKEYS 128
#define EXALLKEYS 100
#define MAX_AINO_HASH 128
#define NOTIFY_HASH_SIZE 127
/* handler state info */
typedef struct _unit {
struct _unit *next;
/* Amiga stuff */
uaecptr dosbase;
uaecptr volume;
uaecptr port; /* Our port */
uaecptr locklist;
/* Native stuff */
uae_s32 unit; /* unit number */
UnitInfo ui; /* unit startup info */
TCHAR 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;
unsigned long total_locked_ainos;
/* ExAll */
ExAllKey exalls[EXALLKEYS];
int exallid;
/* Keys */
struct key *keys;
struct lockrecord *waitingrecords;
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;
struct notify *notifyhash[NOTIFY_HASH_SIZE];
int volflags;
uae_u32 lockkey;
bool inhibited;
bool canremovable;
/* increase when media is changed.
* used to detect if cached aino is valid
*/
int mountcount;
int mount_changed;
struct zvolume *zarchive;
void *cdfs_superblock;
TCHAR *mount_volume;
TCHAR *mount_rootdir;
bool mount_readonly;
int mount_flags;
int reinsertdelay;
TCHAR *newvolume;
TCHAR *newrootdir;
bool newreadonly;
int newflags;
} Unit;
static uae_u32 a_uniq, key_uniq;
typedef uaecptr dpacket;
#define PUT_PCK_RES1(p,v) do { put_long ((p) + dp_Res1, (v)); } while (0)
#define PUT_PCK_RES2(p,v) do { put_long ((p) + dp_Res2, (v)); } while (0)
#define GET_PCK_TYPE(p) ((uae_s32)(get_long ((p) + dp_Type)))
#define GET_PCK_RES1(p) ((uae_s32)(get_long ((p) + dp_Res1)))
#define GET_PCK_RES2(p) ((uae_s32)(get_long ((p) + dp_Res2)))
#define GET_PCK_ARG1(p) ((uae_s32)(get_long ((p) + dp_Arg1)))
#define GET_PCK_ARG2(p) ((uae_s32)(get_long ((p) + dp_Arg2)))
#define GET_PCK_ARG3(p) ((uae_s32)(get_long ((p) + dp_Arg3)))
#define GET_PCK_ARG4(p) ((uae_s32)(get_long ((p) + dp_Arg4)))
#define GET_PCK_ARG5(p) ((uae_s32)(get_long ((p) + dp_Arg5)))
#define PUT_PCK64_RES0(p,v) do { put_long ((p) + dp64_Res0, (v)); } while (0)
#define PUT_PCK64_RES1(p,v) do { put_long ((p) + dp64_Res1, (((uae_u64)v) >> 32)); put_long ((p) + dp64_Res1 + 4, ((uae_u32)v)); } while (0)
#define PUT_PCK64_RES2(p,v) do { put_long ((p) + dp64_Res2, (v)); } while (0)
#define GET_PCK64_TYPE(p) ((uae_s32)(get_long ((p) + dp64_Type)))
#define GET_PCK64_RES0(p) ((uae_s32)(get_long ((p) + dp64_Res0)))
#define GET_PCK64_RES1(p) ( (((uae_s64)(get_long ((p) + dp64_Res1))) << 32) | (((uae_s64)(get_long ((p) + dp64_Res1 + 4))) << 0) )
#define GET_PCK64_ARG1(p) ((uae_s32)(get_long ((p) + dp64_Arg1)))
#define GET_PCK64_ARG2(p) ( (((uae_s64)(get_long ((p) + dp64_Arg2))) << 32) | (((uae_s64)(get_long ((p) + dp64_Arg2 + 4))) << 0) )
#define GET_PCK64_ARG3(p) ((uae_s32)(get_long ((p) + dp64_Arg3)))
#define GET_PCK64_ARG4(p) ((uae_s32)(get_long ((p) + dp64_Arg4)))
#define GET_PCK64_ARG5(p) ( (((uae_s64)(get_long ((p) + dp64_Arg5))) << 32) | (((uae_s64)(get_long ((p) + dp64_Arg5 + 4))) << 0) )
static int flush_cache (Unit *unit, int num);
static TCHAR *char1 (uaecptr addr)
{
static uae_char buf[1024];
static TCHAR bufx[1024];
unsigned int i = 0;
do {
buf[i] = get_byte (addr);
addr++;
} while (buf[i++] && i < sizeof (buf));
return au_fs_copy (bufx, sizeof (bufx) / sizeof (TCHAR), buf);
}
static TCHAR *bstr1 (uaecptr addr)
{
static TCHAR bufx[257];
static uae_char buf[257];
int i;
int n = get_byte (addr);
addr++;
for (i = 0; i < n; i++, addr++)
buf[i] = get_byte (addr);
buf[i] = 0;
return au_fs_copy (bufx, sizeof (bufx) / sizeof (TCHAR), buf);
}
static TCHAR *bstr (Unit *unit, uaecptr addr)
{
int i;
int n = get_byte (addr);
uae_char buf[257];
addr++;
for (i = 0; i < n; i++, addr++)
buf[i] = get_byte (addr);
buf[i] = 0;
au_fs_copy (unit->tmpbuf3, sizeof (unit->tmpbuf3) / sizeof (TCHAR), buf);
return unit->tmpbuf3;
}
static TCHAR *bstr_cut (Unit *unit, uaecptr addr)
{
TCHAR *p = unit->tmpbuf3;
int i, colon_seen = 0, off;
int n = get_byte (addr);
uae_char buf[257];
off = 0;
addr++;
for (i = 0; i < n; i++, addr++) {
uae_u8 c = get_byte (addr);
buf[i] = c;
if (c == '/' || (c == ':' && colon_seen++ == 0))
off = i + 1;
}
buf[i] = 0;
au_fs_copy (unit->tmpbuf3, sizeof (unit->tmpbuf3) / sizeof (TCHAR), buf);
return &p[off];
}
static Unit *units = 0;
static Unit*
find_unit (uaecptr port)
{
Unit* u;
for (u = units; u; u = u->next)
if (u->port == port)
break;
return u;
}
static struct fs_dirhandle *fs_opendir (Unit *u, const TCHAR *nname)
{
struct fs_dirhandle *fsd = xmalloc (struct fs_dirhandle, 1);
{
fsd->od = my_opendir (nname);
if (!fsd->od)
goto end;
}
return fsd;
end:
xfree (fsd);
return NULL;
}
static void fs_closedir (struct fs_dirhandle *fsd)
{
if (!fsd)
return;
my_closedir (fsd->od);
xfree (fsd);
}
static struct fs_filehandle *fs_openfile (Unit *unit, const TCHAR *name, char *flags)
{
struct fs_filehandle *fsf = xmalloc (struct fs_filehandle, 1);
fsf->of = my_open (name, flags);
if (!fsf->of)
goto end;
return fsf;
end:
xfree (fsf);
return NULL;
}
static void fs_closefile (struct fs_filehandle *fd)
{
if (!fd)
return;
my_close (fd->of);
}
static unsigned int fs_read (struct fs_filehandle *fsf, void *b, unsigned int size)
{
return fread (b, 1, size, fsf->of);
}
static unsigned int fs_write (struct fs_filehandle *fsf, void *b, unsigned int size)
{
return fwrite (b, 1, size, fsf->of);
}
static uae_u64 fs_lseek64 (struct fs_filehandle *fsf, uae_s64 offset, int whence)
{
return my_lseek (fsf->of, offset, whence);
}
static uae_u32 fs_lseek (struct fs_filehandle *fsf, uae_s32 offset, int whence)
{
return (uae_u32)my_lseek (fsf->of, (uae_s32)offset, whence);
}
static void set_highcyl (UnitInfo *ui, uae_u32 blocks)
{
uaecptr startup = get_long (ui->devicenode + 7 * 4) << 2;
uaecptr env = get_long (startup + 8) << 2;
put_long (env + 10 * 4, blocks);
}
static void set_volume_name (Unit *unit, uae_u32 ctime)
{
int namelen;
int i;
char *s;
s = ua_fs (unit->ui.volname, -1);
namelen = strlen (s);
put_byte (unit->volume + 44, namelen);
for (i = 0; i < namelen; i++)
put_byte (unit->volume + 45 + i, s[i]);
put_byte (unit->volume + 45 + namelen, 0);
if (ctime) {
long days, mins, ticks;
get_time (ctime, &days, &mins, &ticks);
put_long (unit->volume + 16, days);
put_long (unit->volume + 20, mins);
put_long (unit->volume + 24, ticks);
}
xfree (s);
unit->rootnode.aname = unit->ui.volname;
unit->rootnode.nname = unit->ui.rootdir;
unit->rootnode.mountcount = unit->mountcount;
}
static int filesys_isvolume (Unit *unit)
{
return get_byte (unit->volume + 44) || unit->ui.unknown_media;
}
static void clear_exkeys (Unit *unit)
{
int i;
a_inode *a;
for (i = 0; i < EXKEYS; i++) {
unit->examine_keys[i].aino = 0;
unit->examine_keys[i].curr_file = 0;
unit->examine_keys[i].uniq = 0;
}
for (i = 0; i < EXALLKEYS; i++) {
fs_closedir (unit->exalls[i].dirhandle);
unit->exalls[i].dirhandle = NULL;
xfree (unit->exalls[i].fn);
unit->exalls[i].fn = NULL;
unit->exalls[i].id = 0;
}
unit->exallid = 0;
unit->next_exkey = 1;
a = &unit->rootnode;
while (a) {
a->exnext_count = 0;
if (a->locked_children) {
a->locked_children = 0;
unit->total_locked_ainos--;
}
a = a->next;
if (a == &unit->rootnode)
break;
}
}
static void filesys_delayed_change (Unit *u, int frames, const TCHAR *rootdir, const TCHAR *volume, bool readonly, int flags)
{
u->reinsertdelay = 50;
u->newflags = flags;
u->newreadonly = readonly;
u->newrootdir = my_strdup (rootdir);
if (volume)
u->newvolume = my_strdup (volume);
filesys_eject (u->unit);
if (!rootdir || _tcslen (rootdir) == 0)
u->reinsertdelay = 0;
if (u->reinsertdelay > 0)
write_log (_T("FILESYS: delayed insert %d: '%s' ('%s')\n"), u->unit, volume ? volume : _T("<none>"), rootdir);
}
int filesys_eject (int nr)
{
UnitInfo *ui = &mountinfo.ui[nr];
Unit *u = ui->self;
if (!mountertask || u->mount_changed)
return 0;
if (!ui->open || u == NULL)
return 0;
if (!is_virtual (nr))
return 0;
if (!filesys_isvolume (u))
return 0;
u->mount_changed = -1;
u->mountcount++;
write_log (_T("FILESYS: volume '%s' removal request\n"), u->ui.volname);
// -1 = remove, -2 = remove + remove device node
put_byte (u->volume + 172 - 32, ui->unit_type == UNIT_CDFS ? -1 : -2);
uae_Signal (get_long (u->volume + 176 - 32), 1 << 13);
return 1;
}
static uae_u32 heartbeat;
static int heartbeat_count;
static int heartbeat_task;
// This uses filesystem process to reduce resource usage
void setsystime (void)
{
if (!currprefs.tod_hack)
return;
heartbeat = get_long (rtarea_base + RTAREA_HEARTBEAT);
heartbeat_task = 1;
heartbeat_count = 10;
}
static void setsystime_vblank (void)
{
Unit *u;
for (u = units; u; u = u->next) {
if (is_virtual (u->unit)) {
put_byte (u->volume + 173 - 32, 1);
uae_Signal (get_long (u->volume + 176 - 32), 1 << 13);
break;
}
}
}
int filesys_insert (int nr, TCHAR *volume, const TCHAR *rootdir, bool readonly, int flags)
{
UnitInfo *ui;
Unit *u;
if (!mountertask)
return 0;
if (nr < 0) {
for (u = units; u; u = u->next) {
if (is_virtual (u->unit)) {
if (!filesys_isvolume (u) && mountinfo.ui[u->unit].canremove)
break;
}
}
if (!u) {
for (u = units; u; u = u->next) {
if (is_virtual (u->unit)) {
if (mountinfo.ui[u->unit].canremove)
break;
}
}
}
if (!u)
return 0;
nr = u->unit;
ui = &mountinfo.ui[nr];
} else {
ui = &mountinfo.ui[nr];
u = ui->self;
}
if (!ui->open || u == NULL)
return 0;
if (u->reinsertdelay)
return -1;
if (!is_virtual (nr))
return 0;
if (filesys_isvolume (u)) {
filesys_delayed_change (u, 50, rootdir, volume, readonly, flags);
return -1;
}
u->mountcount++;
u->mount_changed = 1;
u->mount_volume = volume ? my_strdup (volume) : NULL;
u->mount_rootdir = my_strdup (rootdir);
u->mount_readonly = readonly;
u->mount_flags = flags;
put_byte (u->volume + 172 - 32, -3); // wait for insert
uae_Signal (get_long (u->volume + 176 - 32), 1 << 13);
return 100 + nr;
}
static uae_u32 filesys_media_change_reply (TrapContext *ctx, int mode)
{
int nr;
UnitInfo *ui = NULL;
Unit *u;
for (nr = 0; nr < MAX_FILESYSTEM_UNITS; nr++) {
ui = &mountinfo.ui[nr];
u = ui->self;
if (u && u->mount_changed)
break;
}
if (nr >= MAX_FILESYSTEM_UNITS) {
write_log (_T("FILESYS: filesys_media_change_reply without mount_changed flag!?\n"));
return 0;
}
if (u->mount_changed < 0) {
// eject
if (mode == 0) {
write_log (_T("FILESYS: got media change reply, '%s' removal finished\n"), u->ui.volname);
flush_cache (u, -1);
#ifdef SCSI
isofs_unmount (u->ui.cdfs_superblock);
#endif
ui->cdfs_superblock = u->ui.cdfs_superblock = NULL;
zfile_fclose_archive (u->zarchive);
u->zarchive = NULL;
u->ui.unknown_media = false;
#ifdef RETROPLATFORM
if (ui->unit_type == UNIT_CDFS)
rp_cd_image_change (ui->cddevno, NULL);
else
rp_harddrive_image_change (nr, false, NULL);
#endif
} else {
u->mount_changed = 0;
}
return 1;
} else if (u->mount_changed > 0) {
if (mode == 0) {
// insert
uae_u32 ctime = 0;
bool emptydrive = false;
struct uaedev_config_info *uci = NULL;
clear_exkeys (u);
uci = &currprefs.mountconfig[nr];
xfree (u->ui.rootdir);
ui->rootdir = u->ui.rootdir = my_strdup (u->mount_rootdir);
flush_cache (u, -1);
xfree (u->ui.volname);
ui->volname = u->ui.volname = NULL;
#ifdef SCSI
if (ui->unit_type == UNIT_CDFS) {
uae_u64 uniq;
ui->cdfs_superblock = u->ui.cdfs_superblock = isofs_mount (ui->cddevno, &uniq);
u->rootnode.uniq_external = uniq;
u->ui.unknown_media = true;
if (!u->ui.cdfs_superblock)
return 0;
struct isofs_info ii;
set_highcyl (ui, 0);
bool r = isofs_mediainfo (ui->cdfs_superblock, &ii);
if (r && ii.media) {
u->ui.unknown_media = ii.unknown_media;
if (!ii.unknown_media) {
u->ui.volname = ui->volname = my_strdup (ii.volumename);
ctime = ii.creation;
set_highcyl (ui, ii.blocks);
#ifdef RETROPLATFORM
rp_cd_image_change (ui->cddevno, ii.devname);
#endif
}
}
} else
#endif
{
if (set_filesys_volume (u->mount_rootdir, &u->mount_flags, &u->mount_readonly, &emptydrive, &u->zarchive) < 0)
return 0;
if (emptydrive)
return 0;
xfree (u->ui.volname);
ui->volname = u->ui.volname = filesys_createvolname (u->mount_volume, u->mount_rootdir, _T("removable"));
#ifdef RETROPLATFORM
rp_harddrive_image_change (nr, u->mount_readonly, u->mount_rootdir);
#endif
}
if (u->ui.unknown_media) {
write_log (_T("FILESYS: inserted unreadable volume NR=%d RO=%d\n"), nr, u->mount_readonly);
} else {
write_log (_T("FILESYS: inserted volume NR=%d RO=%d '%s' ('%s')\n"), nr, u->mount_readonly, ui->volname, u->mount_rootdir);
set_volume_name (u, ctime);
if (u->mount_flags >= 0)
ui->volflags = u->volflags = u->ui.volflags = u->mount_flags;
_tcscpy (uci->volname, ui->volname);
_tcscpy (uci->rootdir, u->mount_rootdir);
if (u->mount_flags >= 0)
uci->readonly = ui->readonly = u->ui.readonly = u->mount_readonly;
put_byte (u->volume + 44, 0);
put_byte (u->volume + 172 - 32, 1);
}
xfree (u->mount_volume);
xfree (u->mount_rootdir);
u->mount_rootdir = NULL;
u->mount_volume = NULL;
} else {
u->mount_changed = 0;
}
return 1;
}
return 0;
}
int filesys_media_change (const TCHAR *rootdir, int inserted, struct uaedev_config_info *uci)
{
Unit *u;
UnitInfo *ui;
int nr = -1;
TCHAR volname[MAX_DPATH], *volptr;
TCHAR devname[MAX_DPATH];
if (!mountertask)
return 0;
if (automountunit >= 0)
return -1;
nr = -1;
for (u = units; u; u = u->next) {
if (is_virtual (u->unit)) {
ui = &mountinfo.ui[u->unit];
if (ui->rootdir && !memcmp (ui->rootdir, rootdir, _tcslen (rootdir)) && _tcslen (rootdir) + 3 >= _tcslen (ui->rootdir)) {
if (filesys_isvolume (u) && inserted) {
if (uci)
filesys_delayed_change (u, 50, rootdir, uci->volname, uci->readonly, 0);
return 0;
}
nr = u->unit;
break;
}
}
}
ui = NULL;
if (nr >= 0)
ui = &mountinfo.ui[nr];
/* only configured drives have automount support if automount is disabled */
if (/*!currprefs.win32_automount_removable &&*/ (!ui || !ui->configureddrive) && (inserted == 0 || inserted == 1))
return 0;
if (nr < 0 && !inserted)
return 0;
/* already mounted volume was ejected? */
if (nr >= 0 && !inserted)
return filesys_eject (nr);
if (inserted) {
if (uci) {
volptr = my_strdup (uci->volname);
} else {
volname[0] = 0;
//FIXME: target_get_volume_name (&mountinfo, rootdir, volname, MAX_DPATH, 1, 0);
volptr = volname;
if (!volname[0])
volptr = NULL;
if (ui && ui->configureddrive && ui->volname) {
volptr = volname;
_tcscpy (volptr, ui->volname);
}
}
if (!volptr) {
volptr = filesys_createvolname (NULL, rootdir, _T("removable"));
_tcscpy (volname, volptr);
xfree (volptr);
volptr = volname;
}
/* new volume inserted and it was previously mounted? */
if (nr >= 0) {
if (!filesys_isvolume (u)) /* not going to mount twice */
return filesys_insert (nr, volptr, rootdir, false, -1);
return 0;
}
if (inserted < 0) /* -1 = only mount if already exists */
return 0;
/* new volume inserted and it was not previously mounted?
* perhaps we have some empty device slots? */
nr = filesys_insert (-1, volptr, rootdir, 0, 0);
if (nr >= 100) {
if (uci)
uci->configoffset = nr - 100;
return nr;
}
/* nope, uh, need black magic now.. */
if (uci)
_tcscpy (devname, uci->devname);
else
_stprintf (devname, _T("RDH%d"), nr_units ());
nr = add_filesys_unit (devname, volptr, rootdir, 0, 0, 0, 0, 0, 0, 0, 1, NULL, 0, MYVOLUMEINFO_REUSABLE);
if (nr < 0)
return 0;
if (inserted > 1)
mountinfo.ui[nr].canremove = 1;
automountunit = nr;
uae_Signal (mountertask, 1 << 13);
/* poof */
if (uci)
uci->configoffset = nr;
return 100 + nr;
}
return 0;
}
int hardfile_remount (int nr)
{
/* this does work but every media reinsert duplicates the device.. */
#if 0
if (!mountertask)
return 0;
automountunit = nr;
uae_Signal (mountertask, 1 << 13);
#endif
return 1;
}
bool filesys_do_disk_change (int cdunitnum, bool insert)
{
int nr = cdunitnum + cd_unit_offset;
if (!mountinfo.ui[nr].cd_open)
return false;
if (insert) {
filesys_insert (nr, NULL, _T("/"), true, MYVOLUMEINFO_CDFS | MYVOLUMEINFO_READONLY);
return true;
} else {
filesys_eject (nr);
return true;
}
}
/* flags and comments supported? */
static int fsdb_cando (Unit *unit)
{
if (unit->volflags & (MYVOLUMEINFO_ARCHIVE | MYVOLUMEINFO_CDFS))
return 1;
if (currprefs.filesys_custom_uaefsdb && (unit->volflags & MYVOLUMEINFO_STREAMS))
return 1;
if (!currprefs.filesys_no_uaefsdb)
return 1;
return 0;
}
static void prepare_for_open (TCHAR *name)
{
#if 0
struct _stat64 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 void de_recycle_aino (Unit *unit, a_inode *aino)
{
aino_test (aino);
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 (unit->volflags & MYVOLUMEINFO_ARCHIVE) {
;
}
#ifdef SCSI
else if (unit->volflags & MYVOLUMEINFO_CDFS) {
isofs_dispose_inode (unit->ui.cdfs_superblock, aino->uniq_external);
}
#endif
xfree (aino->aname);
xfree (aino->comment);
xfree (aino->nname);
xfree (aino);
}
static void free_all_ainos (Unit *u, a_inode *parent)
{
a_inode *a;
while (a = parent->child) {
free_all_ainos (u, a);
dispose_aino (u, &parent->child, a);
}
}
static int flush_cache (Unit *unit, int num)
{
int i = 0;
int cnt = 100;
//write_log (_T("FILESYS: flushing cache unit %d (max %d items)\n"), unit->unit, num);
if (num == 0)
num = -1;
while (i < num || num < 0) {
int ii = i;
a_inode *parent = unit->rootnode.prev->parent;
a_inode **aip;
aip = &parent->child;
aino_test (parent);
if (parent && !parent->locked_children) {
for (;;) {
a_inode *aino = *aip;
aino_test (aino);
if (aino == 0)
break;
/* Not recyclable if next == 0 (i.e., not chained into
recyclable list), or if parent directory is being
ExNext()ed. */
if (aino->next == 0) {
aip = &aino->sibling;
} else {
if (aino->shlock > 0 || aino->elock)
write_log (_T("panic: freeing locked a_inode!\n"));
de_recycle_aino (unit, aino);
dispose_aino (unit, aip, aino);
i++;
}
}
}
{ //if (unit->rootnode.next != unit->rootnode.prev) {
/* In the previous loop, we went through all children of one
parent. Re-arrange the recycled list so that we'll find a
different parent the next time around.
(infinite loop if there is only one parent?)
*/
int maxloop = 10000;
do {
unit->rootnode.next->prev = unit->rootnode.prev;
unit->rootnode.prev->next = unit->rootnode.next;
unit->rootnode.next = unit->rootnode.prev;
unit->rootnode.prev = unit->rootnode.prev->prev;
unit->rootnode.prev->next = unit->rootnode.next->prev = &unit->rootnode;
} while (unit->rootnode.prev->parent == parent && maxloop-- > 0);
}
if (i == ii)
cnt--;
if (cnt <= 0)
break;
}
return unit->aino_cache_size > 0 ? 0 : 1;
}
static void recycle_aino (Unit *unit, a_inode *new_aino)
{
aino_test (new_aino);
if (new_aino->dir || new_aino->shlock > 0
|| new_aino->elock || new_aino == &unit->rootnode)
/* Still in use */
return;
TRACE2((_T("Recycling; cache size %d, total_locked %d\n"),
unit->aino_cache_size, unit->total_locked_ainos));
if (unit->aino_cache_size > 5000 + unit->total_locked_ainos) {
/* Reap a few. */
flush_cache (unit, 50);
#if 0
{
TCHAR buffer[40];
_stprintf (buffer, "%d ainos reaped.\n", i);
TRACE ((buffer));
}
#endif
}
aino_test (new_aino);
/* 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;
aino_test (new_aino->next);
aino_test (new_aino->prev);
unit->aino_cache_size++;
}
void filesys_flush_cache (void)
{
}
static void update_child_names (Unit *unit, a_inode *a, a_inode *parent)
{
int l0 = _tcslen (parent->nname) + 2;
while (a != 0) {
TCHAR *name_start;
TCHAR *new_name;
TCHAR dirsep[2] = { FSDB_DIR_SEPARATOR, '\0' };
a->parent = parent;
name_start = _tcsrchr (a->nname, FSDB_DIR_SEPARATOR);
if (name_start == 0) {
write_log (_T("malformed file name"));
}
name_start++;
new_name = xmalloc (TCHAR, _tcslen (name_start) + l0);
_tcscpy (new_name, parent->nname);
_tcscat (new_name, dirsep);
_tcscat (new_name, name_start);
xfree (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)
{
aino_test (from);
aino_test (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;
TRACE((_T("deleting aino %x\n"), aino->uniq));
aino_test (aino);
aino->dirty = 1;
aino->deleted = 1;
de_recycle_aino (unit, aino);
/* If any ExKeys are currently pointing at us, advance them. */
if (aino->parent->exnext_count > 0) {
int i;
TRACE((_T("entering exkey validation\n")));
for (i = 0; i < EXKEYS; i++) {
ExamineKey *k = unit->examine_keys + i;
if (k->uniq == 0)
continue;
if (k->aino == aino->parent) {
TRACE((_T("Same parent found for %d\n"), i));
if (k->curr_file == aino) {
k->curr_file = aino->sibling;
TRACE((_T("Advancing curr_file\n")));
}
}
}
}
aip = &aino->parent->child;
while (*aip != aino && *aip != 0)
aip = &(*aip)->sibling;
if (*aip != aino) {
write_log (_T("Couldn't delete aino.\n"));
return;
}
dispose_aino (unit, aip, aino);
}
static a_inode *lookup_sub (a_inode *dir, uae_u32 uniq)
{
a_inode **cp = &dir->child;
a_inode *c, *retval;
for (;;) {
c = *cp;
if (c == 0)
return 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;
}
if (! dir->locked_children) {
/* Move to the front to speed up repeated lookups. Don't do this if
an ExNext is going on in this directory, or we'll terminally
confuse it. */
*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;
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;
aino_test (a);
return a;
}
TCHAR *build_nname (const TCHAR *d, const TCHAR *n)
{
TCHAR dsep[2] = { FSDB_DIR_SEPARATOR, 0 };
TCHAR *p = xmalloc (TCHAR, _tcslen (d) + 1 + _tcslen (n) + 1);
_tcscpy (p, d);
_tcscat (p, dsep);
_tcscat (p, n);
return p;
}
TCHAR *build_aname (const TCHAR *d, const TCHAR *n)
{
TCHAR *p = xmalloc (TCHAR, _tcslen (d) + 1 + _tcslen (n) + 1);
_tcscpy (p, d);
_tcscat (p, _T("/"));
_tcscat (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 TCHAR *get_nname (Unit *unit, a_inode *base, TCHAR *rel, TCHAR **modified_rel, uae_u64 *uniq_ext)
{
TCHAR *found;
TCHAR *p = 0;
*modified_rel = 0;
if (unit->volflags & MYVOLUMEINFO_ARCHIVE) {
//if (zfile_exists_archive(base->nname, rel))
// return build_nname(base->nname, rel);
return NULL;
}
#ifdef SCSI
else if (unit->volflags & MYVOLUMEINFO_CDFS) {
if (isofs_exists (unit->ui.cdfs_superblock, base->uniq_external, rel, uniq_ext))
return build_nname (base->nname, rel);
return NULL;
}
#endif
aino_test (base);
/* 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 TCHAR *create_nname (Unit *unit, a_inode *base, TCHAR *rel)
{
TCHAR *p;
aino_test (base);
/* 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)) {
#if 0
oh_dear:
#endif
if (currprefs.filesys_no_uaefsdb && !(base->volflags & MYVOLUMEINFO_STREAMS)) {
write_log (_T("illegal filename '%s', no stream supporting filesystem and uaefsdb disabled\n"), rel);
return 0;
}
p = fsdb_create_unique_nname (base, rel);
return p;
}
p = build_nname (base->nname, rel);
#if 0
/* Delete this code once we know everything works. */
if (access (p, R_OK) >= 0 || errno != ENOENT) {
write_log (_T("Filesystem in trouble... please report.\n"));
xfree (p);
goto oh_dear;
}
#endif
return p;
}
static int fill_file_attrs (Unit *u, a_inode *base, a_inode *c)
{
/* if (u->volflags & MYVOLUMEINFO_ARCHIVE) {
int isdir, flags;
TCHAR *comment;
zfile_fill_file_attrs_archive (c->nname, &isdir, &flags, &comment);
c->dir = isdir;
c->amigaos_mode = 0;
if (flags >= 0)
c->amigaos_mode = flags;
c->comment = comment;
return 1;
} else*/
#ifdef SCSI
if (u->volflags & MYVOLUMEINFO_CDFS) {
int isdir, flags;
TCHAR *comment;
isofss_fill_file_attrs (u->ui.cdfs_superblock, base->uniq_external, &isdir, &flags, &comment, c->uniq_external);
c->dir = isdir;
c->amigaos_mode = 0;
if (flags >= 0)
c->amigaos_mode = flags;
c->comment = comment;
return 1;
} else
#endif
{
return fsdb_fill_file_attrs (base, c);
}
return 0;
}
/*
* 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 TCHAR *get_aname (Unit *unit, a_inode *base, TCHAR *rel)
{
return my_strdup (rel);
}
static void init_child_aino_tree (Unit *unit, a_inode *base, a_inode *aino)
{
/* Update tree structure */
aino->parent = base;
aino->child = 0;
aino->sibling = base->child;
base->child = aino;
aino->next = aino->prev = 0;
aino->volflags = unit->volflags;
}
static void init_child_aino (Unit *unit, a_inode *base, a_inode *aino)
{
aino->uniq = ++a_uniq;
if (a_uniq == 0xFFFFFFFF) {
write_log (_T("Running out of a_inodes (prepare for big trouble)!\n"));
}
aino->shlock = 0;
aino->elock = 0;
aino->dirty = 0;
aino->deleted = 0;
aino->mountcount = unit->mountcount;
/* For directories - this one isn't being ExNext()ed yet. */
aino->locked_children = 0;
aino->exnext_count = 0;
/* But the parent might be. */
if (base->exnext_count) {
unit->total_locked_ainos++;
base->locked_children++;
}
init_child_aino_tree (unit, base, aino);
aino_test_init (aino);
aino_test (aino);
}
static a_inode *new_child_aino (Unit *unit, a_inode *base, TCHAR *rel)
{
TCHAR *modified_rel;
TCHAR *nn;
a_inode *aino = NULL;
int isvirtual = unit->volflags & (MYVOLUMEINFO_ARCHIVE | MYVOLUMEINFO_CDFS);
TRACE((_T("new_child_aino %s, %s\n"), base->aname, rel));
if (!isvirtual)
aino = fsdb_lookup_aino_aname (base, rel);
if (aino == 0) {
uae_u64 uniq_ext = 0;
nn = get_nname (unit, base, rel, &modified_rel, &uniq_ext);
if (nn == 0)
return 0;
aino = xcalloc (a_inode, 1);
if (aino == 0)
return 0;
aino->uniq_external = uniq_ext;
aino->aname = modified_rel ? modified_rel : my_strdup (rel);
aino->nname = nn;
aino->comment = 0;
aino->has_dbentry = 0;
if (!fill_file_attrs (unit, base, aino)) {
xfree (aino);
return 0;
}
if (aino->dir && !isvirtual)
fsdb_clean_dir (aino);
}
init_child_aino (unit, base, aino);
recycle_aino (unit, aino);
TRACE((_T("created aino %x, lookup, amigaos_mode %d\n"), aino->uniq, aino->amigaos_mode));
return aino;
}
static a_inode *create_child_aino (Unit *unit, a_inode *base, TCHAR *rel, int isdir)
{
a_inode *aino = xcalloc (a_inode, 1);
if (aino == 0)
return 0;
aino->nname = create_nname (unit, base, rel);
if (!aino->nname) {
free (aino);
return 0;
}
aino->aname = my_strdup (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((_T("created aino %x, create\n"), aino->uniq));
return aino;
}
static a_inode *lookup_child_aino (Unit *unit, a_inode *base, TCHAR *rel, int *err)
{
a_inode *c = base->child;
int l0 = _tcslen (rel);
aino_test (base);
aino_test (c);
if (base->dir == 0) {
*err = ERROR_OBJECT_WRONG_TYPE;
return 0;
}
while (c != 0) {
int l1 = _tcslen (c->aname);
if (l0 <= l1 && same_aname (rel, c->aname + l1 - l0)
&& (l0 == l1 || c->aname[l1-l0-1] == '/') && c->mountcount == unit->mountcount)
break;
c = c->sibling;
}
if (c != 0)
return c;
c = new_child_aino (unit, base, rel);
if (c == 0)
*err = ERROR_OBJECT_NOT_AROUND;
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, TCHAR *rel, uae_u32 *err, uae_u64 uniq_external)
{
a_inode *c = base->child;
int l0 = _tcslen (rel);
int isvirtual = unit->volflags & (MYVOLUMEINFO_ARCHIVE | MYVOLUMEINFO_CDFS);
aino_test (base);
aino_test (c);
*err = 0;
while (c != 0) {
int l1 = _tcslen (c->nname);
/* Note: using _tcscmp here. */
if (l0 <= l1 && _tcscmp (rel, c->nname + l1 - l0) == 0
&& (l0 == l1 || c->nname[l1-l0-1] == FSDB_DIR_SEPARATOR) && c->mountcount == unit->mountcount)
break;
c = c->sibling;
}
if (c != 0)
return c;
if (!isvirtual)
c = fsdb_lookup_aino_nname (base, rel);
if (c == 0) {
c = xcalloc (a_inode, 1);
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->uniq_external = uniq_external;
c->has_dbentry = 0;
if (!fill_file_attrs (unit, base, c)) {
xfree (c);
*err = ERROR_NO_FREE_STORE;
return 0;
}
if (c->dir && !isvirtual)
fsdb_clean_dir (c);
}
init_child_aino (unit, base, c);
recycle_aino (unit, c);
TRACE((_T("created aino %x, exnext\n"), c->uniq));
return c;
}
static a_inode *get_aino (Unit *unit, a_inode *base, const TCHAR *rel, int *err)
{
TCHAR *tmp;
TCHAR *p;
a_inode *curr;
int i;
aino_test (base);
*err = 0;
TRACE((_T("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;
TCHAR *component_end;
component_end = _tcschr (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_AROUND || component_end != 0)
curr = 0;
/* ? what error is appropriate? */
break;
}
curr = next;
if (component_end)
p = component_end+1;
else
break;
}
}
xfree (tmp);
return curr;
}
static uae_u32 notifyhash (const TCHAR *s)
{
uae_u32 hash = 0;
while (*s)
hash = (hash << 5) + *s++;
return hash % NOTIFY_HASH_SIZE;
}
static Notify *new_notify (Unit *unit, TCHAR *name)
{
Notify *n = xmalloc (Notify, 1);
uae_u32 hash = notifyhash (name);
n->next = unit->notifyhash[hash];
unit->notifyhash[hash] = n;
n->partname = name;
return n;
}
#if 0
static void free_notify_item (Notify *n)
{
xfree (n->fullname);
xfree (n->partname);
xfree (n);
}
#endif
static void free_notify (Unit *unit, int hash, Notify *n)
{
Notify *n1, *prev = 0;
for (n1 = unit->notifyhash[hash]; n1; n1 = n1->next) {
if (n == n1) {
if (prev)
prev->next = n->next;
else
unit->notifyhash[hash] = n->next;
break;
}
prev = n1;
}
}
static void startup_update_unit (Unit *unit, UnitInfo *uinfo)
{
if (!unit)
return;
xfree (unit->ui.volname);
// memcpy (&unit->ui, uinfo, sizeof UnitInfo);
unit->ui.devname = uinfo->devname;
unit->ui.volname = my_strdup (uinfo->volname); /* might free later for rename */
}
static Unit *startup_create_unit (UnitInfo *uinfo, int num)
{
int i;
Unit *unit, *u;
unit = xcalloc (Unit, 1);
/* keep list in insertion order */
u = units;
if (u) {
while (u->next)
u = u->next;
u->next = unit;
} else {
units = unit;
}
uinfo->self = unit;
unit->volume = 0;
unit->port = m68k_areg (regs, 5);
unit->unit = num;
startup_update_unit (unit, uinfo);
unit->cmds_complete = 0;
unit->cmds_sent = 0;
unit->cmds_acked = 0;
clear_exkeys (unit);
unit->total_locked_ainos = 0;
unit->keys = 0;
for (i = 0; i < NOTIFY_HASH_SIZE; i++) {
Notify *n = unit->notifyhash[i];
while (n) {
Notify *n2 = n;
n = n->next;
xfree (n2->fullname);
xfree (n2->partname);
xfree (n2);
}
unit->notifyhash[i] = 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->rootnode.comment = 0;
unit->rootnode.has_dbentry = 0;
unit->rootnode.volflags = uinfo->volflags;
aino_test_init (&unit->rootnode);
unit->aino_cache_size = 0;
for (i = 0; i < MAX_AINO_HASH; i++)
unit->aino_hash[i] = 0;
return unit;
}
#ifdef UAE_FILESYS_THREADS
static void *filesys_thread (void *unit_v);
#endif
static void filesys_start_thread (UnitInfo *ui, int nr)
{
ui->unit_pipe = 0;
ui->back_pipe = 0;
ui->reset_state = FS_STARTUP;
if (!isrestore ()) {
ui->startup = 0;
ui->self = 0;
}
#ifdef UAE_FILESYS_THREADS
if (is_virtual (nr)) {
ui->unit_pipe = xmalloc (smp_comm_pipe, 1);
ui->back_pipe = xmalloc (smp_comm_pipe, 1);
init_comm_pipe (ui->unit_pipe, 100, 3);
init_comm_pipe (ui->back_pipe, 100, 1);
uae_start_thread (_T("filesys"), filesys_thread, (void *)ui, &ui->tid);
}
#endif
if (isrestore ())
startup_update_unit (ui->self, ui);
}
static uae_u32 REGPARAM2 startup_handler (TrapContext *context)
{
/* Just got the startup packet. It's in A4. DosBase is in A2,
* our allocated volume structure is in A3, 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);
uaecptr devnode;
int nr;
TCHAR *devname = bstr1 (get_long (pkt + dp_Arg1) << 2);
TCHAR *s;
Unit *unit;
UnitInfo *uinfo;
int late = 0;
int ed, ef;
uae_u64 uniq = 0;
uae_u32 cdays, ctime = 0;
/* find UnitInfo with correct device name */
s = _tcschr (devname, ':');
if (s)
*s = '\0';
for (nr = 0; nr < MAX_FILESYSTEM_UNITS; nr++) {
/* Hardfile volume name? */
if (!mountinfo.ui[nr].open)
continue;
if (!is_virtual (nr))
continue;
if (mountinfo.ui[nr].startup == arg2)
break;
}
if (nr == MAX_FILESYSTEM_UNITS) {
write_log (_T("Failed attempt to mount device '%s'\n"), devname);
put_long (pkt + dp_Res1, DOS_FALSE);
put_long (pkt + dp_Res2, ERROR_DEVICE_NOT_MOUNTED);
return 0;
}
uinfo = mountinfo.ui + nr;
cdays = 3800 + nr;
#ifdef SCSI
if (uinfo->unit_type == UNIT_CDFS) {
ed = ef = 0;
uinfo->cddevno = i - cd_unit_offset;
if (!sys_command_open (uinfo->cddevno)) {
write_log (_T("Failed attempt to open CD unit %d\n"), uinfo->cddevno);
put_long (pkt + dp_Res1, DOS_FALSE);
put_long (pkt + dp_Res2, ERROR_DEVICE_NOT_MOUNTED);
return 0;
}
uinfo->cdfs_superblock = isofs_mount(uinfo->cddevno, &uniq);
uinfo->wasisempty = true;
struct isofs_info ii;
if (isofs_mediainfo (uinfo->cdfs_superblock, &ii)) {
xfree (uinfo->volname);
if (ii.media) {
uinfo->wasisempty = false;
if (!ii.unknown_media) {
uinfo->volname = my_strdup (ii.volumename);
ctime = ii.creation;
set_highcyl (uinfo, ii.totalblocks);
}
}
uinfo->unknown_media = ii.unknown_media;
}
uinfo->cd_open = true;
} else
#endif
{
ed = my_existsdir (uinfo->rootdir);
ef = my_existsfile (uinfo->rootdir);
if (!uinfo->wasisempty && !ef && !ed) {
write_log (_T("Failed attempt to mount device '%s'\n"), devname);
put_long (pkt + dp_Res1, DOS_FALSE);
put_long (pkt + dp_Res2, ERROR_DEVICE_NOT_MOUNTED);
return 0;
}
}
if (!uinfo->unit_pipe) {
late = 1;
filesys_start_thread (uinfo, nr);
}
unit = startup_create_unit (uinfo, nr);
unit->volflags = uinfo->volflags;
unit->rootnode.uniq_external = uniq;
/* write_comm_pipe_int (unit->ui.unit_pipe, -1, 1);*/
write_log (_T("FS: %s (flags=%08X,E=%d,ED=%d,EF=%d,native='%s') starting..\n"),
unit->ui.volname, unit->volflags, uinfo->wasisempty, ed, ef, unit->ui.rootdir);
/* fill in our process in the device node */
devnode = get_long (pkt + dp_Arg3) << 2;
put_long (devnode + 8, unit->port);
unit->dosbase = m68k_areg (regs, 2);
/* make new volume */
unit->volume = m68k_areg (regs, 3) + 32;
put_long (unit->volume + 180 - 32, devnode);
#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;
put_long (unit->dummy_message + 10, 0);
/* Prepare volume information */
put_long (unit->volume + 4, 2); /* Type = dt_volume */
put_long (unit->volume + 12, 0); /* Lock */
put_long (unit->volume + 16, cdays); /* 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 */
put_byte (unit->volume + 44, 0);
if (!uinfo->wasisempty && !uinfo->unknown_media) {
/* Set volume if non-empty */
set_volume_name (unit, ctime);
fsdb_clean_dir (&unit->rootnode);
}
put_long (unit->volume + 8, unit->port);
put_long (unit->volume + 32, uinfo->unit_type == UNIT_CDFS ? DISK_TYPE_DOS : DISK_TYPE_DOS_FFS);
put_long (pkt + dp_Res1, DOS_TRUE);
return 1 | (late ? 2 : 0);
}
static void
do_info (Unit *unit, dpacket packet, uaecptr info, bool disk_info)
{
struct fs_usage fsu;
int ret, err = ERROR_NO_FREE_STORE;
int blocksize, nr;
uae_u32 dostype;
bool fs = false;
blocksize = 512;
/* not FFS because it is not understood by WB1.x C:Info */
dostype = DISK_TYPE_DOS;
nr = unit->unit;
if (unit->volflags & MYVOLUMEINFO_ARCHIVE) {
ret = zfile_fs_usage_archive (unit->ui.rootdir, 0, &fsu);
fs = true;
#ifdef SCSI
} else if (unit->volflags & MYVOLUMEINFO_CDFS) {
struct isofs_info ii;
ret = isofs_mediainfo (unit->ui.cdfs_superblock, &ii) ? 0 : 1;
fsu.fsu_blocks = ii.blocks;
fsu.fsu_bavail = 0;
blocksize = ii.blocksize;
nr = unit->unit - cd_unit_offset;
#endif
} else {
ret = get_fs_usage (unit->ui.rootdir, 0, &fsu);
if (ret)
err = dos_errno ();
fs = true;
}
if (ret != 0) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, err);
return;
}
put_long (info, 0); /* errors */
put_long (info + 4, nr); /* unit number */
put_long (info + 8, unit->ui.readonly || unit->ui.locked ? 80 : 82); /* state */
put_long (info + 20, blocksize); /* bytesperblock */
if (disk_info && unit->ui.unknown_media) {
put_long (info + 12, 0);
put_long (info + 16, 0);
put_long (info + 24, ('B' << 24) | ('A' << 16) | ('D' << 8) | (0 << 0)); /* ID_UNREADABLE_DISK */
put_long (info + 28, 0);
} else if (disk_info && !filesys_isvolume (unit)) {
put_long (info + 12, 0);
put_long (info + 16, 0);
put_long (info + 24, -1); /* ID_NO_DISK_PRESENT */
put_long (info + 28, 0);
} else {
put_long (info + 12, fsu.fsu_blocks ); /* numblocks */
put_long (info + 16, fsu.fsu_blocks - fsu.fsu_bavail); /* inuse */
put_long (info + 24, dostype); /* disk type */
put_long (info + 28, unit->volume >> 2); /* volume node */
}
put_long (info + 32, 0); /* inuse */
PUT_PCK_RES1 (packet, DOS_TRUE);
}
static void
action_disk_info (Unit *unit, dpacket packet)
{
TRACE((_T("ACTION_DISK_INFO\n")));
do_info (unit, packet, GET_PCK_ARG1 (packet) << 2, true);
}
static void
action_info (Unit *unit, dpacket packet)
{
TRACE((_T("ACTION_INFO\n")));
do_info (unit, packet, GET_PCK_ARG2 (packet) << 2, false);
}
static void free_key (Unit *unit, Key *k)
{
Key *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;
}
struct lockrecord *lr;
for (lr = k->record; lr;) {
struct lockrecord *next = lr->next;
xfree (lr);
lr = next;
}
if (k->fd != NULL)
fs_closefile (k->fd);
xfree(k);
}
static Key *lookup_key (Unit *unit, uae_u32 uniq)
{
Key *k;
unsigned int total = 0;
/* 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) {
total++;
if (uniq == k->uniq)
return k;
}
write_log (_T("Error: couldn't find key %u / %u!\n"), uniq, total);
/* There isn't much hope we will recover. Unix would kill the process,
* AmigaOS gets killed by it. */
write_log (_T("Better reset that Amiga - the system is messed up.\n"));
return 0;
}
static Key *new_key (Unit *unit)
{
Key *k = xcalloc (Key, 1);
k->uniq = ++key_uniq;
k->fd = NULL;
k->file_pos = 0;
k->next = unit->keys;
unit->keys = k;
return k;
}
#if TRACING_ENABLED
static void
dumplock (Unit *unit, uaecptr lock)
{
a_inode *a;
TRACE((_T("LOCK: 0x%lx"), lock));
if (!lock) {
TRACE((_T("\n")));
return;
}
TRACE((_T("{ 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((_T("not found!")));
} else {
TRACE((_T("%s"), a->nname));
}
TRACE((_T(" }\n")));
}
#endif
static a_inode *find_aino (Unit *unit, uaecptr lock, const TCHAR *name, int *err)
{
a_inode *a;
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 {
TRACE((_T("aino: 0x%08lx"), (unsigned long int)olda->uniq));
TRACE((_T(" \"%s\"\n"), olda->nname));
a = get_aino (unit, olda, name, err);
}
} else {
a = get_aino (unit, &unit->rootnode, name, err);
}
if (a) {
TRACE((_T("aino=\"%s\"\n"), a->nname));
}
aino_test (a);
return a;
}
static uaecptr make_lock (Unit *unit, uae_u32 uniq, long mode)
{
/* allocate lock from the list kept by the assembly code */
uaecptr lock;
lock = get_long (unit->locklist);
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);
/* prepend to lock chain */
put_long (lock, get_long (unit->volume + 28));
put_long (unit->volume + 28, lock >> 2);
DUMPLOCK(unit, lock);
return lock;
}
#define NOTIFY_CLASS 0x40000000
#define NOTIFY_CODE 0x1234
#ifndef TARGET_AMIGAOS
#define NRF_SEND_MESSAGE 1
#define NRF_SEND_SIGNAL 2
#define NRF_WAIT_REPLY 8
#define NRF_NOTIFY_INITIAL 16
#define NRF_MAGIC (1 << 31)
#endif
static void notify_send (Unit *unit, Notify *n)
{
uaecptr nr = n->notifyrequest;
int flags = get_long (nr + 12);
if (flags & NRF_SEND_MESSAGE) {
if (!(flags & NRF_WAIT_REPLY) || ((flags & NRF_WAIT_REPLY) && !(flags & NRF_MAGIC))) {
uae_NotificationHack (unit->port, nr);
} else if (flags & NRF_WAIT_REPLY) {
put_long (nr + 12, get_long (nr + 12) | NRF_MAGIC);
}
} else if (flags & NRF_SEND_SIGNAL) {
uae_Signal (get_long (nr + 16), 1 << get_byte (nr + 20));
}
}
static void notify_check (Unit *unit, a_inode *a)
{
Notify *n;
int hash = notifyhash (a->aname);
for (n = unit->notifyhash[hash]; n; n = n->next) {
uaecptr nr = n->notifyrequest;
if (same_aname (n->partname, a->aname)) {
int err;
a_inode *a2 = find_aino (unit, 0, n->fullname, &err);
if (err == 0 && a == a2)
notify_send (unit, n);
}
}
if (a->parent) {
hash = notifyhash (a->parent->aname);
for (n = unit->notifyhash[hash]; n; n = n->next) {
uaecptr nr = n->notifyrequest;
if (same_aname (n->partname, a->parent->aname)) {
int err;
a_inode *a2 = find_aino (unit, 0, n->fullname, &err);
if (err == 0 && a->parent == a2)
notify_send (unit, n);
}
}
}
}
static void
action_add_notify (Unit *unit, dpacket packet)
{
uaecptr nr = GET_PCK_ARG1 (packet);
int flags;
Notify *n;
TCHAR *name, *p, *partname;
TRACE((_T("ACTION_ADD_NOTIFY\n")));
name = my_strdup (char1 (get_long (nr + 4)));
flags = get_long (nr + 12);
if (!(flags & (NRF_SEND_MESSAGE | NRF_SEND_SIGNAL))) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_BAD_NUMBER);
return;
}
#if 0
write_log (_T("Notify:\n"));
write_log (_T("nr_Name '%s'\n"), char1 (get_long (nr + 0)));
write_log (_T("nr_FullName '%s'\n"), name);
write_log (_T("nr_UserData %08X\n"), get_long (nr + 8));
write_log (_T("nr_Flags %08X\n"), flags);
if (flags & NRF_SEND_MESSAGE) {
write_log (_T("Message NotifyRequest, port = %08X\n"), get_long (nr + 16));
} else if (flags & NRF_SEND_SIGNAL) {
write_log (_T("Signal NotifyRequest, Task = %08X signal = %d\n"), get_long (nr + 16), get_long (nr + 20));
} else {
write_log (_T("corrupt NotifyRequest\n"));
}
#endif
p = name + _tcslen (name) - 1;
if (p[0] == ':')
p--;
while (p > name && p[0] != ':' && p[0] != '/')
p--;
if (p[0] == ':' || p[0] == '/')
p++;
partname = my_strdup (p);
n = new_notify (unit, partname);
n->notifyrequest = nr;
n->fullname = name;
if (flags & NRF_NOTIFY_INITIAL) {
int err;
a_inode *a = find_aino (unit, 0, n->fullname, &err);
if (err == 0)
notify_send (unit, n);
}
PUT_PCK_RES1 (packet, DOS_TRUE);
}
static void
action_remove_notify (Unit *unit, dpacket packet)
{
uaecptr nr = GET_PCK_ARG1 (packet);
Notify *n;
int hash;
TRACE((_T("ACTION_REMOVE_NOTIFY\n")));
for (hash = 0; hash < NOTIFY_HASH_SIZE; hash++) {
for (n = unit->notifyhash[hash]; n; n = n->next) {
if (n->notifyrequest == nr) {
//write_log (_T("NotifyRequest %08X freed\n"), n->notifyrequest);
xfree (n->fullname);
xfree (n->partname);
free_notify (unit, hash, n);
PUT_PCK_RES1 (packet, DOS_TRUE);
return;
}
}
}
write_log (_T("Tried to free non-existing NotifyRequest %08X\n"), nr);
PUT_PCK_RES1 (packet, DOS_TRUE);
}
static void free_lock (Unit *unit, uaecptr lock)
{
if (! lock)
return;
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 next = 0;
while (current) {
next = get_long (current << 2);
if (lock == next << 2)
break;
current = next;
}
if (!current) {
write_log (_T("tried to unlock non-existing lock %x\n"), lock);
return;
}
put_long (current << 2, get_long (lock));
}
lock -= 4;
put_long (lock, get_long (unit->locklist));
put_long (unit->locklist, lock);
}
static void
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);
a_inode *a;
int err;
if (mode != SHARED_LOCK && mode != EXCLUSIVE_LOCK) {
TRACE((_T("Bad mode %d (should be %d or %d).\n"), mode, SHARED_LOCK, EXCLUSIVE_LOCK));
mode = SHARED_LOCK;
}
TRACE((_T("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 != SHARED_LOCK && 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 == SHARED_LOCK)
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, dpacket packet)
{
uaecptr lock = GET_PCK_ARG1 (packet) << 2;
a_inode *a;
TRACE((_T("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_AROUND);
return;
}
if (a->elock)
a->elock = 0;
else
a->shlock--;
recycle_aino (unit, a);
free_lock(unit, lock);
PUT_PCK_RES1 (packet, DOS_TRUE);
}
static uaecptr
action_dup_lock_2 (Unit *unit, dpacket packet, uae_u32 uniq)
{
uaecptr out;
a_inode *a;
a = lookup_aino (unit, uniq);
if (a == 0) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_OBJECT_NOT_AROUND);
return 0;
}
/* 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 0;
}
a->shlock++;
de_recycle_aino (unit, a);
out = make_lock (unit, a->uniq, -2) >> 2;
PUT_PCK_RES1 (packet, out);
return out;
}
static void
action_dup_lock (Unit *unit, dpacket packet)
{
uaecptr lock = GET_PCK_ARG1 (packet) << 2;
TRACE((_T("ACTION_DUP_LOCK(0x%lx)\n"), lock));
if (!lock) {
PUT_PCK_RES1 (packet, 0);
return;
}
action_dup_lock_2 (unit, packet, get_long (lock + 4));
}
static void
action_lock_from_fh (Unit *unit, dpacket packet)
{
Key *k = lookup_key (unit, GET_PCK_ARG1 (packet));
TRACE((_T("ACTION_COPY_DIR_FH(0x%lx,'%s')\n"), GET_PCK_ARG1 (packet), k ? k->aino->aname : _T("<null>")));
if (k == 0) {
PUT_PCK_RES1 (packet, DOS_FALSE);
return;
}
action_dup_lock_2 (unit, packet, k->aino->uniq);
}
#if !defined TARGET_AMIGAOS || !defined WORDS_BIGENDIAN
/* convert time_t to/from AmigaDOS time */
static const int secs_per_day = 24 * 60 * 60;
static const int diff = (8 * 365 + 2) * (24 * 60 * 60);
void
get_time (time_t t, long* days, long* mins, long* ticks)
{
/* time_t is secs since 1-1-1970 */
/* days since 1-1-1978 */
/* mins since midnight */
/* ticks past minute @ 50Hz */
# if !defined _WIN32 && !defined BEOS && !defined TARGET_AMIGAOS
/*
* On Unix-like systems, t is in UTC. The Amiga
* requires local time, so we have to take account of
* this difference if we can. This ain't easy to do in
* a portable, thread-safe way.
*/
# if defined HAVE_LOCALTIME_R && defined HAVE_TIMEGM
struct tm tm;
/* Convert t to local time */
localtime_r (&t, &tm);
/* Calculate local time in seconds since the Unix Epoch */
t = timegm (&tm);
# endif
# endif
/* Adjust for difference between Unix and Amiga epochs */
t -= diff;
if (t < 0)
t = 0;
/* Calculate Amiga date-stamp */
*days = t / secs_per_day;
t -= *days * secs_per_day;
*mins = t / 60;
t -= *mins * 60;
*ticks = t * 50;
}
/*
* Convert Amiga date-stamp to host time in seconds since the start
* of the Unix epoch
*/
static time_t
put_time (long days, long mins, long ticks)
{
time_t t;
if (days < 0)
days = 0;
if (days > 9900 * 365)
days = 9900 * 365; // in future far enough?
if (mins < 0 || mins >= 24 * 60)
mins = 0;
if (ticks < 0 || ticks >= 60 * 50)
ticks = 0;
t = ticks / 50;
t += mins * 60;
t += ((uae_u64)days) * secs_per_day;
t += diff;
# if !defined _WIN32 && !defined BEOS
/*
* t is still in local time zone. For Unix-like systems
* we need a time in UTC, so we have to take account of
* the difference if we can. This ain't easy to do in
* a portable, thread-safe way.
*/
# if defined HAVE_GMTIME_R && defined HAVE_LOCALTIME_R
{
struct tm tm;
struct tm now_tm;
time_t now_t;
gmtime_r (&t, &tm);
/*
* tm now contains the desired time in local time zone, not taking account
* of DST. To fix this, we determine if DST is in effect now and stuff that
* into tm.
*/
now_t = time (0);
localtime_r (&now_t, &now_tm);
tm.tm_isdst = now_tm.tm_isdst;
/* Convert time to UTC in seconds since the Unix epoch */
t = mktime (&tm);
}
# endif
# endif
return t;
}
#endif
static void free_exkey (Unit *unit, ExamineKey *ek)
{
if (--ek->aino->exnext_count == 0) {
TRACE ((_T("Freeing ExKey and reducing total_locked from %d by %d\n"),
unit->total_locked_ainos, ek->aino->locked_children));
unit->total_locked_ainos -= ek->aino->locked_children;
ek->aino->locked_children = 0;
}
ek->aino = 0;
ek->uniq = 0;
}
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 (_T("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, a_inode *aino)
{
uae_u32 uniq;
uae_u32 oldest = 0xFFFFFFFE;
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 (_T("Houston, we have a problem (%s).\n"), aino->nname);
free_exkey (unit, oldest_ek);
ek = oldest_ek;
found:
uniq = unit->next_exkey;
if (uniq >= 0xFFFFFFFE) {
/* Things will probably go wrong, but most likely the Amiga will crash
* before this happens because of something else. */
uniq = 1;
}
unit->next_exkey = uniq + 1;
ek->aino = aino;
ek->curr_file = 0;
ek->uniq = uniq;
return ek;
}
static void move_exkeys (Unit *unit, a_inode *from, a_inode *to)
{
int i;
unsigned long tmp = 0;
for (i = 0; i < EXKEYS; i++) {
ExamineKey *k = unit->examine_keys + i;
if (k->uniq == 0)
continue;
if (k->aino == from) {
k->aino = to;
tmp++;
}
}
if (tmp != from->exnext_count)
write_log (_T("filesys.c: Bug in ExNext bookkeeping. BAD.\n"));
to->exnext_count = from->exnext_count;
to->locked_children = from->locked_children;
from->exnext_count = 0;
from->locked_children = 0;
}
static void
get_fileinfo (Unit *unit, dpacket packet, uaecptr info, a_inode *aino)
{
struct _stat64 statbuf;
long days, mins, ticks;
int i, n, entrytype, blocksize;
int fsdb_can = fsdb_cando (unit);
TCHAR *xs;
char *x, *x2;
memset (&statbuf, 0, sizeof statbuf);
/* No error checks - this had better work. */
/* if (unit->volflags & MYVOLUMEINFO_ARCHIVE)
zfile_stat_archive (aino->nname, &statbuf);
else if (unit->volflags & MYVOLUMEINFO_CDFS)
isofs_stat (unit->ui.cdfs_superblock, aino->uniq_external, &statbuf);
else*/
stat (aino->nname, &statbuf);
if (aino->parent == 0) {
/* Guru book says ST_ROOT = 1 (root directory, not currently used)
* but some programs really expect 2 from root dir..
*/
entrytype = 2;
xs = unit->ui.volname;
} else {
entrytype = aino->dir ? 2 : -3;
xs = aino->aname;
}
put_long (info + 4, entrytype);
/* AmigaOS docs say these have to contain the same value. */
put_long (info + 120, entrytype);
TRACE((_T("name=\"%s\"\n"), xs));
x = ua_fs (xs, -1);
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++;
#if defined TARGET_AMIGAOS && defined WORDS_BIGENDIAN
BPTR lock;
struct FileInfoBlock fib __attribute__((aligned(4)));
if ((lock = Lock (aino->nname, SHARED_LOCK))) {
Examine (lock, &fib);
UnLock (lock);
}
put_long (info + 124, fib.fib_Size);
put_long (info + 128, fib.fib_NumBlocks);
put_long (info + 132, fib.fib_Date.ds_Days);
put_long (info + 136, fib.fib_Date.ds_Minute);
put_long (info + 140, fib.fib_Date.ds_Tick);
#else
put_long (info + 116, fsdb_can ? aino->amigaos_mode : fsdb_mode_supported (aino));
put_long (info + 124, statbuf.st_size > MAXFILESIZE32 ? MAXFILESIZE32 : statbuf.st_size);
#ifdef HAVE_ST_BLOCKS
put_long (info + 128, statbuf.st_blocks);
#else
blocksize = (unit->volflags & MYVOLUMEINFO_CDFS) ? 2048 : 512;
put_long (info + 128, (statbuf.st_size + blocksize - 1) / blocksize);
#endif
get_time (statbuf.st_mtime, &days, &mins, &ticks);
put_long (info + 132, days);
put_long (info + 136, mins);
put_long (info + 140, ticks);
#endif
if (aino->comment == 0 || !fsdb_can)
put_long (info + 144, 0);
else {
TRACE((_T("comment=\"%s\"\n"), aino->comment));
i = 144;
xs = aino->comment;
if (!xs)
xs= _T("");
x = ua_fs (xs, -1);
n = strlen (x);
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);
}
int get_native_path (uae_u32 lock, TCHAR *out)
{
int i = 0;
for (i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (mountinfo.ui[i].self) {
a_inode *a = lookup_aino (mountinfo.ui[i].self, get_long ((lock << 2) + 4));
if (a) {
_tcscpy (out, a->nname);
return 0;
}
}
}
return -1;
}
#define REC_EXCLUSIVE 0
#define REC_EXCLUSIVE_IMMED 1
#define REC_SHARED 2
#define REC_SHARED_IMMED 3
static struct lockrecord *new_record (uae_u32 packet, uae_u32 pos, uae_u32 len, uae_u32 mode, uae_u32 timeout, uae_u32 msg)
{
struct lockrecord *lr = xcalloc (struct lockrecord, 1);
lr->packet = packet;
lr->pos = pos;
lr->len = len;
lr->mode = mode;
lr->timeout = timeout * vblank_hz / 50;
lr->msg = msg;
return lr;
}
static bool record_hit (Unit *unit, Key *k, uae_u32 pos, uae_u32 len, uae_u32 mode)
{
Key *k2;
struct lockrecord *lr;
bool exclusive = mode == REC_EXCLUSIVE || mode == REC_EXCLUSIVE_IMMED;
for (k2 = unit->keys; k2; k2 = k2->next) {
if (k2->aino->uniq == k->aino->uniq) {
if (k2 == k)
continue;
for (lr = k2->record; lr; lr = lr->next) {
bool exclusive2 = lr->mode == REC_EXCLUSIVE || lr->mode == REC_EXCLUSIVE_IMMED;
if (exclusive || exclusive2) {
uae_u32 a1 = pos;
uae_u32 a2 = pos + len;
uae_u32 b1 = lr->pos;
uae_u32 b2 = lr->pos + lr->len;
if (len && lr->len) {
bool hit = (a1 >= b1 && a1 < b2) || (a2 > b1 && a2 < b2) || (b1 >= a1 && b1 < a2) || (b2 > a1 && b2 < a2);
if (hit)
return true;
}
}
}
}
}
return false;
}
static void record_timeout (Unit *unit)
{
bool retry = true;
struct lockrecord *lr;
while (retry) {
retry = false;
struct lockrecord *prev = NULL;
for (lr = unit->waitingrecords; lr; lr = lr->next) {
lr->timeout--;
if (lr->timeout == 0) {
Key *k = lookup_key (unit, GET_PCK_ARG1 (lr->packet));
PUT_PCK_RES1 (lr->packet, DOS_FALSE);
PUT_PCK_RES2 (lr->packet, ERROR_LOCK_TIMEOUT);
// mark packet as complete
put_long (lr->msg + 4, 0xfffffffe);
uae_Signal (get_long (unit->volume + 176 - 32), 1 << 13);
if (prev)
prev->next = lr->next;
else
unit->waitingrecords = lr->next;
write_log (_T("queued record timed out '%s',%d,%d,%d,%d\n"), k ? k->aino->nname : _T("NULL"), lr->pos, lr->len, lr->mode, lr->timeout);
xfree (lr);
retry = true;
break;
}
prev = lr;
}
}
}
static void record_check_waiting (Unit *unit)
{
bool retry = true;
struct lockrecord *lr;
while (retry) {
retry = false;
struct lockrecord *prev = NULL;
for (lr = unit->waitingrecords; lr; lr = lr->next) {
Key *k = lookup_key (unit, GET_PCK_ARG1 (lr->packet));
if (!k || !record_hit (unit, k, lr->pos, lr->len, lr->mode)) {
if (prev)
prev->next = lr->next;
else
unit->waitingrecords = lr->next;
write_log (_T("queued record released '%s',%d,%d,%d,%d\n"), k->aino->nname, lr->pos, lr->len, lr->mode, lr->timeout);
// mark packet as complete
put_long (lr->msg + 4, 0xffffffff);
xfree (lr);
retry = true;
break;
}
prev = lr;
}
}
}
static int action_lock_record (Unit *unit, dpacket packet, uae_u32 msg)
{
Key *k = lookup_key (unit, GET_PCK_ARG1 (packet));
uae_u32 pos = GET_PCK_ARG2 (packet);
uae_u32 len = GET_PCK_ARG3 (packet);
uae_u32 mode = GET_PCK_ARG4 (packet);
uae_u32 timeout = GET_PCK_ARG5 (packet);
bool exclusive = mode == REC_EXCLUSIVE || mode == REC_EXCLUSIVE_IMMED;
write_log (_T("action_lock_record('%s',%d,%d,%d,%d)\n"), k ? k->aino->nname : _T("null"), pos, len, mode, timeout);
if (!k || mode > REC_SHARED_IMMED) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_OBJECT_WRONG_TYPE);
return 1;
}
if (mode == REC_EXCLUSIVE_IMMED || mode == REC_SHARED_IMMED)
timeout = 0;
if (record_hit (unit, k, pos, len, mode)) {
if (timeout && msg) {
// queue it and do not reply
struct lockrecord *lr = new_record (packet, pos, len, mode, timeout, msg);
if (unit->waitingrecords) {
lr->next = unit->waitingrecords;
unit->waitingrecords = lr;
} else {
unit->waitingrecords = lr;
}
write_log (_T("-> collision, timeout queued\n"));
return -1;
}
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_LOCK_COLLISION);
write_log (_T("-> ERROR_LOCK_COLLISION\n"));
return 1;
}
struct lockrecord *lr = new_record (GET_PCK_ARG1 (packet), pos, len, mode, timeout, 0);
if (k->record) {
lr->next = k->record;
k->record = lr;
} else {
k->record = lr;
}
PUT_PCK_RES1 (packet, DOS_TRUE);
write_log (_T("-> OK\n"));
return 1;
}
static void action_free_record (Unit *unit, dpacket packet)
{
Key *k = lookup_key (unit, GET_PCK_ARG1 (packet));
uae_u32 pos = GET_PCK_ARG2 (packet);
uae_u32 len = GET_PCK_ARG3 (packet);
write_log (_T("action_free_record('%s',%d,%d)\n"), k ? k->aino->nname : _T("null"), pos, len);
if (!k) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_OBJECT_WRONG_TYPE);
return;
}
struct lockrecord *prev = NULL;
struct lockrecord *lr;
for (lr = k->record; lr; lr = lr->next) {
if (lr->pos == pos && lr->len == len) {
if (prev)
prev->next = lr->next;
else
k->record = lr->next;
xfree (lr);
write_log (_T("->OK\n"));
record_check_waiting (unit);
PUT_PCK_RES1 (packet, DOS_TRUE);
return;
}
}
write_log (_T("-> ERROR_RECORD_NOT_LOCKED\n"));
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_RECORD_NOT_LOCKED);
}
#define EXALL_DEBUG 0
#define EXALL_END 0xde1111ad
static ExAllKey *getexall (Unit *unit, uaecptr control, int id)
{
int i;
if (id < 0) {
for (i = 0; i < EXALLKEYS; i++) {
if (unit->exalls[i].id == 0) {
unit->exallid++;
if (unit->exallid == EXALL_END)
unit->exallid++;
unit->exalls[i].id = unit->exallid;
unit->exalls[i].control = control;
return &unit->exalls[i];
}
}
} else if (id > 0) {
for (i = 0; i < EXALLKEYS; i++) {
if (unit->exalls[i].id == id)
return &unit->exalls[i];
}
}
return NULL;
}
static int exalldo (uaecptr exalldata, uae_u32 exalldatasize, uae_u32 type, uaecptr control, Unit *unit, a_inode *aino)
{
uaecptr exp = exalldata;
int i;
int size, size2;
int entrytype;
TCHAR *xs = NULL, *commentx = NULL;
uae_u32 flags = 15;
long days, mins, ticks;
struct _stat64 statbuf;
int fsdb_can = fsdb_cando (unit);
uae_u16 uid = 0, gid = 0;
char *x = NULL, *comment = NULL;
int ret = 0;
memset (&statbuf, 0, sizeof statbuf);
/* if (unit->volflags & MYVOLUMEINFO_ARCHIVE)
zfile_stat_archive (aino->nname, &statbuf);
else if (unit->volflags & MYVOLUMEINFO_CDFS)
isofs_stat (unit->ui.cdfs_superblock, aino->uniq_external, &statbuf);
else*/
stat (aino->nname, &statbuf);
if (aino->parent == 0) {
entrytype = 2;
xs = unit->ui.volname;
} else {
entrytype = aino->dir ? 2 : -3;
xs = aino->aname;
}
x = ua_fs (xs, -1);
size = 0;
size2 = 4;
if (type >= 1) {
size2 += 4;
size += strlen (x) + 1;
size = (size + 3) & ~3;
}
if (type >= 2)
size2 += 4;
if (type >= 3)
size2 += 4;
if (type >= 4) {
flags = fsdb_can ? aino->amigaos_mode : fsdb_mode_supported (aino);
size2 += 4;
}
if (type >= 5) {
get_time (statbuf.st_mtime, &days, &mins, &ticks);
size2 += 12;
}
if (type >= 6) {
size2 += 4;
if (aino->comment == 0 || !fsdb_can)
commentx = _T("");
else
commentx = aino->comment;
comment = ua_fs (commentx, -1);
size += strlen (comment) + 1;
size = (size + 3) & ~3;
}
if (type >= 7) {
size2 += 4;
uid = 0;
gid = 0;
}
i = get_long (control + 0);
while (i > 0) {
exp = get_long (exp); /* ed_Next */
i--;
}
if (exalldata + exalldatasize - exp < size + size2)
goto end; /* not enough space */
#if EXALL_DEBUG > 0
write_log (_T("ID=%d, %d, %08x: '%s'%s\n"),
get_long (control + 4), get_long (control + 0), exp, xs, aino->dir ? _T(" [DIR]") : _T(""));
#endif
put_long (exp, exp + size + size2); /* ed_Next */
if (type >= 1) {
put_long (exp + 4, exp + size2);
for (i = 0; i <= strlen (x); i++) {
put_byte (exp + size2, x[i]);
size2++;
}
}
if (type >= 2)
put_long (exp + 8, entrytype);
if (type >= 3)
put_long (exp + 12, statbuf.st_size > MAXFILESIZE32 ? MAXFILESIZE32 : statbuf.st_size);
if (type >= 4)
put_long (exp + 16, flags);
if (type >= 5) {
put_long (exp + 20, days);
put_long (exp + 24, mins);
put_long (exp + 28, ticks);
}
if (type >= 6) {
put_long (exp + 32, exp + size2);
put_byte (exp + size2, strlen (comment));
for (i = 0; i <= strlen (comment); i++) {
put_byte (exp + size2, comment[i]);
size2++;
}
}
if (type >= 7) {
put_word (exp + 36, uid);
put_word (exp + 38, gid);
}
put_long (control + 0, get_long (control + 0) + 1);
ret = 1;
end:
//xaind: xfree (x);
xfree (comment);
return ret;
}
static int action_examine_all_do (Unit *unit, uaecptr lock, ExAllKey *eak, uaecptr exalldata, uae_u32 exalldatasize, uae_u32 type, uaecptr control)
{
a_inode *aino, *base = NULL;
struct dirent *ok;
uae_u32 err;
struct fs_dirhandle *d;
TCHAR fn[MAX_DPATH];
if (lock != 0)
base = lookup_aino (unit, get_long (lock + 4));
if (base == 0)
base = &unit->rootnode;
for (;;) {
uae_u64 uniq = 0;
d = eak->dirhandle;
if (!eak->fn) {
do {
/* if (d->fstype == FS_ARCHIVE)
else if (d->fstype == FS_DIRECTORY)
ok = my_readdir (d->od, fn);
else if (d->fstype == FS_CDFS)
ok = isofs_readdir (d->isod, fn, &uniq);
else*/
ok = readdir (d->od);
} while (ok && d->fstype == FS_DIRECTORY && fsdb_name_invalid (ok->d_name));
if (!ok)
return 0;
} else {
_tcscpy (ok->d_name, eak->fn);
xfree (eak->fn);
eak->fn = NULL;
}
aino = lookup_child_aino_for_exnext (unit, base, ok->d_name, &err, uniq);
if (!aino)
return 0;
eak->id = unit->exallid++;
put_long (control + 4, eak->id);
if (!exalldo (exalldata, exalldatasize, type, control, unit, aino)) {
eak->fn = my_strdup (fn); /* no space in exallstruct, save current entry */
break;
}
}
return 1;
}
static int action_examine_all_end (Unit *unit, dpacket packet)
{
uae_u32 id;
uae_u32 doserr = 0;
ExAllKey *eak;
uaecptr control = GET_PCK_ARG5 (packet);
if (kickstart_version < 36)
return 0;
id = get_long (control + 4);
eak = getexall (unit, control, id);
#if EXALL_DEBUG > 0
write_log (_T("EXALL_END ID=%d %x\n"), id, eak);
#endif
if (!eak) {
write_log (_T("FILESYS: EXALL_END non-existing ID %d\n"), id);
doserr = ERROR_OBJECT_WRONG_TYPE;
} else {
eak->id = 0;
fs_closedir (eak->dirhandle);
xfree (eak->fn);
eak->fn = NULL;
eak->dirhandle = NULL;
}
if (doserr) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, doserr);
} else {
PUT_PCK_RES1 (packet, DOS_TRUE);
}
return 1;
}
static int action_examine_all (Unit *unit, dpacket packet)
{
uaecptr lock = GET_PCK_ARG1 (packet) << 2;
uaecptr exalldata = GET_PCK_ARG2 (packet);
uae_u32 exalldatasize = GET_PCK_ARG3 (packet);
uae_u32 type = GET_PCK_ARG4 (packet);
uaecptr control = GET_PCK_ARG5 (packet);
ExAllKey *eak = NULL;
a_inode *base = NULL;
struct fs_dirhandle *d;
int ok, i;
uaecptr exp;
uae_u32 id, doserr = ERROR_NO_MORE_ENTRIES;
ok = 0;
#if EXALL_DEBUG > 0
write_log (_T("exall: %08x %08x-%08x %d %d %08x\n"),
lock, exalldata, exalldata + exalldatasize, exalldatasize, type, control);
write_log (_T("exall: MatchString %08x, MatchFunc %08x\n"),
get_long (control + 8), get_long (control + 12));
#endif
put_long (control + 0, 0); /* eac_Entries */
/* EXAMINE ALL might use dos.library MatchPatternNoCase() which is >=36 */
if (kickstart_version < 36)
return 0;
if (type == 0 || type > 7) {
doserr = ERROR_BAD_NUMBER;
goto fail;
}
PUT_PCK_RES1 (packet, DOS_TRUE);
id = get_long (control + 4);
if (id == EXALL_END) {
write_log (_T("FILESYS: EXALL called twice with ERROR_NO_MORE_ENTRIES\n"));
goto fail; /* already ended exall() */
}
if (id) {
eak = getexall (unit, control, id);
if (!eak) {
write_log (_T("FILESYS: EXALL non-existing ID %d\n"), id);
doserr = ERROR_OBJECT_WRONG_TYPE;
goto fail;
}
if (!action_examine_all_do (unit, lock, eak, exalldata, exalldatasize, type, control))
goto fail;
if (get_long (control + 0) == 0) {
/* uh, no space for first entry.. */
doserr = ERROR_NO_FREE_STORE;
goto fail;
}
} else {
eak = getexall (unit, control, -1);
if (!eak)
goto fail;
if (lock != 0)
base = lookup_aino (unit, get_long (lock + 4));
if (base == 0)
base = &unit->rootnode;
#if EXALL_DEBUG > 0
write_log("exall: ID=%d '%s'\n", eak->id, base->nname);
#endif
d = fs_opendir (unit, base->nname);
if (!d)
goto fail;
eak->dirhandle = d;
put_long (control + 4, eak->id);
if (!action_examine_all_do (unit, lock, eak, exalldata, exalldatasize, type, control))
goto fail;
if (get_long (control + 0) == 0) {
/* uh, no space for first entry.. */
doserr = ERROR_NO_FREE_STORE;
goto fail;
}
}
ok = 1;
fail:
/* Clear last ed_Next. This "list" is quite non-Amiga like.. */
exp = exalldata;
i = get_long (control + 0);
for (;;) {
if (i <= 1) {
if (exp)
put_long (exp, 0);
break;
}
exp = get_long (exp); /* ed_Next */
i--;
}
#if EXALL_DEBUG > 0
write_log("ok=%d, err=%d, eac_Entries = %d\n", ok, ok ? -1 : doserr, get_long (control + 0));
#endif
if (!ok) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, doserr);
if (eak) {
eak->id = 0;
fs_closedir (eak->dirhandle);
eak->dirhandle = NULL;
xfree (eak->fn);
eak->fn = NULL;
}
if (doserr == ERROR_NO_MORE_ENTRIES)
put_long (control + 4, EXALL_END);
}
return 1;
}
static uae_u32 exall_helpder(TrapContext *context)
{
int i;
Unit *u;
uaecptr packet = m68k_areg (regs, 4);
uaecptr control = get_long (packet + dp_Arg5);
uae_u32 id = get_long (control + 4);
#if EXALL_DEBUG > 0
write_log (_T("FILESYS: EXALL extra round ID=%d\n"), id);
#endif
if (id == EXALL_END)
return 1;
for (u = units; u; u = u->next) {
for (i = 0; i < EXALLKEYS; i++) {
if (u->exalls[i].id == id && u->exalls[i].control == control) {
action_examine_all (u, packet);
}
}
}
return 1;
}
static uae_u32 REGPARAM2 fsmisc_helper (TrapContext *context)
{
int mode = m68k_dreg (regs, 0);
uae_u32 t;
switch (mode)
{
case 0:
return exall_helpder (context);
case 1:
return filesys_media_change_reply (context, 0);
case 2:
return filesys_media_change_reply (context, 1);
case 3:
t = getlocaltime ();
uae_u32 secs = (uae_u32)t - (8 * 365 + 2) * 24 * 60 * 60;
return secs;
}
return 0;
}
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((_T("ACTION_EXAMINE_OBJECT(0x%lx,0x%lx)\n"), lock, info));
DUMPLOCK(unit, lock);
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);
}
/* Read a directory's contents, create a_inodes for each file, and
mark them as locked in memory so that recycle_aino will not reap
them.
We do this to avoid problems with the host OS: we don't want to
leave the directory open on the host side until all ExNext()s have
finished - they may never finish! */
static void populate_directory (Unit *unit, a_inode *base)
{
struct fs_dirhandle *d;
a_inode *aino;
d = fs_opendir (unit, base->nname);
if (!d)
return;
for (aino = base->child; aino; aino = aino->sibling) {
base->locked_children++;
unit->total_locked_ainos++;
}
TRACE2((_T("Populating directory, child %p, locked_children %d\n"),
base->child, base->locked_children));
for (;;) {
uae_u64 uniq = 0;
TCHAR fn[MAX_DPATH];
struct dirent *ok;
uae_u32 err;
/* Find next file that belongs to the Amiga fs (skipping things
like "..", "." etc. */
do {
/* if (d->fstype == FS_ARCHIVE)
ok = zfile_readdir_archive (d->zd, fn);
else if (d->fstype == FS_DIRECTORY)
ok = my_readdir (d->od, fn);
else if (d->fstype == FS_CDFS)
ok = isofs_readdir (d->isod, fn, &uniq);
else*/
ok = readdir (d->od);
} while (ok /*&& !d->isarch*/ && fsdb_name_invalid (ok->d_name));
if (!ok)
break;
/* This calls init_child_aino, which will notice that the parent is
being ExNext()ed, and it will increment the locked counts. */
aino = lookup_child_aino_for_exnext (unit, base, ok->d_name, &err, uniq);
}
fs_closedir (d);
}
static void do_examine (Unit *unit, dpacket packet, ExamineKey *ek, uaecptr info)
{
for (;;) {
TCHAR *name;
if (ek->curr_file == 0)
break;
name = ek->curr_file->nname;
get_fileinfo (unit, packet, info, ek->curr_file);
ek->curr_file = ek->curr_file->sibling;
if (!(unit->volflags & (MYVOLUMEINFO_ARCHIVE | MYVOLUMEINFO_CDFS)) && !fsdb_exists(name)) {
TRACE ((_T("%s orphaned"), name));
continue;
}
TRACE ((_T("curr_file set to %p %s\n"), ek->curr_file,
ek->curr_file ? ek->curr_file->aname : _T("NULL")));
return;
}
TRACE((_T("no more entries\n")));
free_exkey (unit, ek);
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES);
}
static void action_examine_next (Unit *unit, dpacket packet)
{
uaecptr lock = GET_PCK_ARG1 (packet) << 2;
uaecptr info = GET_PCK_ARG2 (packet) << 2;
a_inode *aino = 0;
ExamineKey *ek;
uae_u32 uniq;
TRACE((_T("ACTION_EXAMINE_NEXT(0x%lx,0x%lx)\n"), lock, info));
gui_flicker_led (LED_HD, unit->unit, 1);
DUMPLOCK(unit, lock);
if (lock != 0)
aino = lookup_aino (unit, get_long (lock + 4));
if (aino == 0)
aino = &unit->rootnode;
for(;;) {
uniq = get_long (info);
if (uniq == 0) {
write_log (_T("ExNext called for a file! (Houston?)\n"));
goto no_more_entries;
} else if (uniq == 0xFFFFFFFE)
goto no_more_entries;
else if (uniq == 0xFFFFFFFF) {
TRACE((_T("Creating new ExKey\n")));
ek = new_exkey (unit, aino);
if (ek) {
if (aino->exnext_count++ == 0)
populate_directory (unit, aino);
ek->curr_file = aino->child;
TRACE((_T("Initial curr_file: %p %s\n"), ek->curr_file,
ek->curr_file ? ek->curr_file->aname : _T("NULL")));
}
} else {
TRACE((_T("Looking up ExKey\n")));
ek = lookup_exkey (unit, get_long (info));
}
if (ek == 0) {
write_log (_T("Couldn't find a matching ExKey. Prepare for trouble.\n"));
goto no_more_entries;
}
put_long (info, ek->uniq);
if (!ek->curr_file || ek->curr_file->mountcount == unit->mountcount)
break;
ek->curr_file = ek->curr_file->sibling;
if (!ek->curr_file)
goto no_more_entries;
}
do_examine (unit, packet, ek, info);
return;
no_more_entries:
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_NO_MORE_ENTRIES);
}
static void do_find (Unit *unit, dpacket packet, int mode, int create, int fallback)
{
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;
struct fs_filehandle *fd;
int err;
char *openmode;
int aino_created = 0;
int isvirtual = unit->volflags & (MYVOLUMEINFO_ARCHIVE | MYVOLUMEINFO_CDFS);
TRACE((_T("ACTION_FIND_*(0x%lx,0x%lx,\"%s\",%d,%d)\n"), fh, lock, bstr (unit, name), mode, create));
TRACE((_T("fh=%x lock=%x name=%x\n"), fh, lock, name));
DUMPLOCK(unit, lock);
aino = find_aino (unit, lock, bstr (unit, name), &err);
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);
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 || unit->ui.locked)
&& 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 || unit->ui.locked)) {
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;
}
prepare_for_open (aino->nname);
openmode = ((mode & A_FIBF_READ) == 0 ? "wb"
: (mode & A_FIBF_WRITE) == 0 ? "rb"
: "r+b");
if (create) openmode = "w+b";
fd = fs_openfile (unit, aino->nname, openmode);
if (fd == NULL) {
if (aino_created)
delete_aino (unit, aino);
PUT_PCK_RES1 (packet, DOS_FALSE);
/* archive and fd == NULL = corrupt archive or out of memory */
PUT_PCK_RES2 (packet, isvirtual ? ERROR_OBJECT_NOT_AROUND : dos_errno ());
return;
}
k = new_key (unit);
k->fd = fd;
k->aino = aino;
k->dosmode = mode;
k->createmode = create;
k->notifyactive = create ? 1 : 0;
if (create && isvirtual)
fsdb_set_file_attrs (aino);
put_long (fh + 36, k->uniq);
if (create == 2) {
aino->elock = 1;
// clear comment if file already existed
if (aino->comment) {
xfree (aino->comment);
aino->comment = 0;
}
fsdb_set_file_attrs (aino);
} else {
aino->shlock++;
}
de_recycle_aino (unit, aino);
PUT_PCK_RES1 (packet, DOS_TRUE);
}
static void
action_fh_from_lock (Unit *unit, dpacket packet)
{
uaecptr fh = GET_PCK_ARG1 (packet) << 2;
uaecptr lock = GET_PCK_ARG2 (packet) << 2;
a_inode *aino;
Key *k;
struct fs_filehandle *fd;
char *openmode;
int mode;
TRACE((_T("ACTION_FH_FROM_LOCK(0x%lx,0x%lx)\n"), fh, lock));
DUMPLOCK(unit,lock);
if (!lock) {
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;
mode = aino->amigaos_mode; /* Use same mode for opened filehandle as existing Lock() */
prepare_for_open (aino->nname);
TRACE ((_T(" mode is %d\n"), mode));
openmode = (((mode & A_FIBF_READ) ? "wb"
: (mode & A_FIBF_WRITE) ? "rb"
: "r+b"));
/* the files on CD really can have the write-bit set. */
if (unit->ui.readonly || unit->ui.locked)
openmode = "rb";
fd = fs_openfile (unit, aino->nname, openmode);
if (fd == NULL) {
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);
/* I don't think I need to play with shlock count here, because I'm
opening from an existing lock ??? */
de_recycle_aino (unit, aino);
free_lock (unit, lock); /* lock must be unlocked */
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, dpacket packet)
{
do_find (unit, packet, A_FIBF_READ | A_FIBF_WRITE, 0, 1);
}
static void
action_find_output (Unit *unit, dpacket packet)
{
if (unit->ui.readonly || unit->ui.locked) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED);
return;
}
do_find (unit, packet, A_FIBF_READ | A_FIBF_WRITE, 2, 0);
}
static void
action_find_write (Unit *unit, dpacket packet)
{
if (unit->ui.readonly || unit->ui.locked) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED);
return;
}
do_find (unit, packet, A_FIBF_READ | A_FIBF_WRITE, 1, 0);
}
/* change file/dir's parent dir modification time */
static void updatedirtime (a_inode *a1, int now)
{
#if !defined TARGET_AMIGAOS || !defined WORDS_BIGENDIAN
struct _stat64 statbuf;
struct utimbuf ut;
long days, mins, ticks;
if (!a1->parent)
return;
if (!now) {
if (stat (a1->nname, &statbuf) == -1)
return;
get_time (statbuf.st_mtime, &days, &mins, &ticks);
ut.actime = ut.modtime = put_time (days, mins, ticks);
utime (a1->parent->nname, &ut);
} else {
utime (a1->parent->nname, NULL);
}
#endif
}
static void
action_end (Unit *unit, dpacket packet)
{
Key *k;
TRACE((_T("ACTION_END(0x%lx)\n"), GET_PCK_ARG1 (packet)));
k = lookup_key (unit, GET_PCK_ARG1 (packet));
if (k != 0) {
if (k->notifyactive) {
notify_check (unit, k->aino);
updatedirtime (k->aino, 1);
}
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, dpacket packet)
{
Key *k = lookup_key (unit, GET_PCK_ARG1 (packet));
uaecptr addr = GET_PCK_ARG2 (packet);
uae_u32 size = GET_PCK_ARG3 (packet);
uae_u32 actual = 0;
if (k == 0) {
PUT_PCK_RES1 (packet, DOS_FALSE);
/* PUT_PCK_RES2 (packet, EINVAL); */
return;
}
TRACE((_T("ACTION_READ(%s,0x%lx,%ld)\n"), k->aino->nname, addr, size));
gui_flicker_led (LED_HD, unit->unit, 1);
if (size == 0) {
actual = 0;
PUT_PCK_RES1 (packet, 0);
PUT_PCK_RES2 (packet, 0);
} else if (valid_address (addr, size)) {
uae_u8 *realpt = get_real_address (addr);
actual = fs_read (k->fd, realpt, size);
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 {
PUT_PCK_RES1 (packet, actual);
k->file_pos += actual;
}
flush_dcache (addr, size);
} else {
uae_u8 *buf;
off_t old, filesize;
write_log (_T("unixfs warning: Bad pointer passed for read: %08x, size %d\n"), addr, size);
/* ugh this is inefficient but easy */
old = fs_lseek (k->fd, 0, SEEK_CUR);
filesize = fs_lseek (k->fd, 0, SEEK_END);
fs_lseek (k->fd, old, SEEK_SET);
if (size > filesize)
size = filesize;
buf = xmalloc (uae_u8, size);
if (!buf) {
PUT_PCK_RES1 (packet, -1);
PUT_PCK_RES2 (packet, ERROR_NO_FREE_STORE);
return;
}
actual = fs_read (k->fd, buf, size);
if (actual < 0) {
PUT_PCK_RES1 (packet, 0);
PUT_PCK_RES2 (packet, dos_errno ());
} else {
int i;
PUT_PCK_RES1 (packet, actual);
for (i = 0; i < actual; i++)
put_byte (addr + i, buf[i]);
k->file_pos += actual;
}
xfree (buf);
flush_dcache (addr, size);
size = 0;
}
TRACE((_T("=%d\n"), actual));
}
static void
action_write (Unit *unit, dpacket packet)
{
Key *k = lookup_key (unit, GET_PCK_ARG1 (packet));
uaecptr addr = GET_PCK_ARG2 (packet);
uae_u32 size = GET_PCK_ARG3 (packet);
uae_u32 actual;
uae_u8 *buf;
int i;
if (k == 0) {
PUT_PCK_RES1 (packet, DOS_FALSE);
/* PUT_PCK_RES2 (packet, EINVAL); */
return;
}
gui_flicker_led (LED_HD, unit->unit, 2);
TRACE((_T("ACTION_WRITE(%s,0x%lx,%ld)\n"), k->aino->nname, addr, size));
if (unit->ui.readonly || unit->ui.locked) {
PUT_PCK_RES1 (packet, DOS_FALSE);
PUT_PCK_RES2 (packet, ERROR_DISK_WRITE_PROTECTED);
return;
}
if (size == 0) {
actual = 0;
PUT_PCK_RES1 (packet, 0);
PUT_PCK_RES2 (packet, 0);
} else if (valid_address (addr, size)) {
uae_u8 *realpt = get_real_address (addr);
if (fs_lseek64 (k->fd, k->file_pos, SEEK_SET) < 0) {
PUT_PCK_RES1 (packet, 0);
PUT_PCK_RES2 (packet, dos_errno ());
return;
}
actual = fs_write (k->fd, realpt, size);
} else {
write_log (_T("unixfs warning: Bad pointer passed for write: %08x, size %d\n"), addr, size);
/* ugh this is inefficient but easy */
if (fs_lseek64 (k->fd, k->file_pos, SEEK_SET) < 0) {
PUT_PCK_RES1 (packet, 0);
PUT_PCK_RES2 (packet, dos_errno ());
return;
}
buf = xmalloc (uae_u8, size);
if (!buf) {
PUT_PCK_RES1 (packet, -1);
PUT_PCK_RES2 (packet, ERROR_NO_FREE_STORE);
return;
}
for (i = 0; i < size; i++)
buf[i] = get_byte (addr + i);
actual = fs_write (k->fd, buf, size);
xfree (buf);
}
TRACE((_T("=%d\n"), actual));
PUT_PCK_RES1 (packet, actual);
if (actual != size)