~rabbits/public-inbox

Ensure that all file operations correctly wrap in memory. v1 PROPOSED

d6: 1
 Ensure that all file operations correctly wrap in memory.

 1 files changed, 108 insertions(+), 75 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rabbits/public-inbox/patches/55132/mbox | git am -3
Learn more about email & git

[PATCH] Ensure that all file operations correctly wrap in memory. Export this patch

From: d_m <d_m@plastic-idolatry.com>

The Varvara spec says that memory accesses should wrap.
Previously the file device ignored this by attempting to
truncate all reads and writes that would go beyond 0xffff.
There may be cases where memory outside uxn.ram could be
accessed.

After this change, all addresses read from and written to
should correctly be wrapped (i.e. uxn.ram[addr & 0xffff]).

This change also fixes a bug with hexadecimal sizes which
the previous patch introduced.
---
 src/devices/file.c | 183 ++++++++++++++++++++++++++-------------------
 1 file changed, 108 insertions(+), 75 deletions(-)

diff --git a/src/devices/file.c b/src/devices/file.c
index 56a1baf..583a54b 100644
--- a/src/devices/file.c
+++ b/src/devices/file.c
@@ -76,26 +76,70 @@ reset(UxnFile *c)
}

static Uint16
get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
stat_fill(Uint16 addr, Uint16 len, char c)
{
	Uint16 i;
	for (i = 0; i < len; i++)
		uxn.ram[(addr + i) & 0xffff] = c;
	return len;
}

static Uint16
stat_size(Uint16 addr, Uint16 len, off_t size)
{
	Uint16 i;
	for (i = 1; i <= len; i++) {
		char c = '0' + (Uint8)(size & 0xf);
		if (c > '9') c += 39;
		uxn.ram[(addr + len - i) & 0xffff] = c;
		size = size >> 4;
	}
	if (size > 0)
		stat_fill(addr, len, '?');
	return len;
}

static Uint16
stat_entry(Uint16 addr, char sym, off_t size, const char *s)
{
	Uint16 pos = addr;
	pos += sym ? stat_fill(pos, 4, sym) : stat_size(pos, 4, size);
	uxn.ram[(pos++) & 0xffff] = ' ';
	while(*s)
		uxn.ram[(pos++) & 0xffff] = *(s++);
	if (sym == '-')
		uxn.ram[(pos++) & 0xffff] = '/';
	uxn.ram[(pos++) & 0xffff] = '\n';
	return pos - addr;
}

static Uint16
get_entry(Uint16 addr, Uint16 len, const char *pathname, const char *basename)
{
	struct stat st;
	if(len < strlen(basename) + 8)
		return 0;
	int row_size = strlen(basename) + 6; /* 4 bytes, ' ', basename, '\n' */
	char sym = 0;
	off_t size = 0;

	if(stat(pathname, &st))
		return fail_nonzero ? snprintf(p, len, "!!!! %s\n", basename) : 0;
	else if(S_ISDIR(st.st_mode))
		return snprintf(p, len, "---- %s/\n", basename);
	else if(st.st_size < 0x10000)
		return snprintf(p, len, "%04x %s\n", (unsigned int)st.st_size, basename);
		sym = '!';
	else if(S_ISDIR(st.st_mode)) {
		sym = '-';
		row_size++; /* directories have a '/' suffix */
	} else if(st.st_size >= 0x10000)
		sym = '?';
	else
		return snprintf(p, len, "???? %s\n", basename);
		size = st.st_size;

	return len < row_size ? 0 : stat_entry(addr, sym, size, basename);
}

static Uint16
file_read_dir(UxnFile *c, char *dest, Uint16 len)
file_read_dir(UxnFile *c, Uint16 addr, Uint16 len)
{
	static char pathname[4352];
	char *p = dest;
	Uint16 pos = addr;
	char cwd[PATH_MAX] = {'\0'}, *t;
	if(c->de == NULL) c->de = readdir(c->dir);
	for(; c->de != NULL; c->de = readdir(c->dir)) {
		Uint16 n;
@@ -103,7 +147,6 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
			continue;
		if(strcmp(c->de->d_name, "..") == 0) {
			/* hide "sandbox/.." */
			char cwd[PATH_MAX] = {'\0'}, *t;
			/* Note there's [currently] no way of chdir()ing from uxn, so $PWD
			 * is always the sandbox top level. */
			getcwd(cwd, sizeof(cwd));
@@ -119,12 +162,12 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
			snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
		else
			pathname[0] = '\0';
		n = get_entry(p, len, pathname, c->de->d_name, 1);
		n = get_entry(pos, len, pathname, c->de->d_name);
		if(!n) break;
		p += n;
		pos += n;
		len -= n;
	}
	return p - dest;
	return pos - addr;
}

static char *
@@ -176,26 +219,35 @@ file_check_sandbox(UxnFile *c)
}

static Uint16
file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox)
file_init(UxnFile *c, Uint16 addr)
{
	char *p = c->current_filename;
	size_t len = sizeof(c->current_filename);
	Uint16 i = addr;
	reset(c);
	if(len > max_len) len = max_len;
	while(len) {
		if((*p++ = *filename++) == '\0') {
			if(!override_sandbox) /* override sandbox for loading roms */
				file_check_sandbox(c);
	for(;; p++, i++) {
		if((*p = uxn.ram[i & 0xffff]) == '\0') {
			file_check_sandbox(c);
			return 0;
		}
		len--;
	}
	c->current_filename[0] = '\0';
	return 0;
}

static Uint16
file_read(UxnFile *c, void *dest, int len)
file_read_file(Uint16 addr, int len, FILE *f)
{
	int total = addr + len;
	if (total <= 0x10000) {
		return fread(&uxn.ram[addr], 1, len, f);
	} else {
		size_t n1 = fread(&uxn.ram[addr], 1, 0x10000 - addr, f);
		size_t n2 = fread(&uxn.ram[0], 1, total - 0x10000, f);
		return n1 + n2;
	}
}

static Uint16
file_read(UxnFile *c, Uint16 addr, int len)
{
	if(c->outside_sandbox) return 0;
	if(c->state != FILE_READ && c->state != DIR_READ) {
@@ -206,9 +258,9 @@ file_read(UxnFile *c, void *dest, int len)
			c->state = FILE_READ;
	}
	if(c->state == FILE_READ)
		return fread(dest, 1, len, c->f);
		return file_read_file(addr, len, c->f);
	if(c->state == DIR_READ)
		return file_read_dir(c, dest, len);
		return file_read_dir(c, addr, len);
	return 0;
}

@@ -245,7 +297,22 @@ ensure_parent_dirs(char *p)
}

static Uint16
file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
file_write_file(Uint16 addr, int len, FILE *f)
{
	int total = addr + len;
	fflush(stdout);
	if (total <= 0x10000) {
		Uint16 n = fwrite(&uxn.ram[addr], 1, len, f);
		return fflush(f) == 0 ? n : 0;
	} else {
		Uint16 n1 = fwrite(&uxn.ram[addr], 1, 0x10000 - addr, f);
		Uint16 n2 = fwrite(&uxn.ram[0], 1, total - 0x10000, f);
		return fflush(f) == 0 ? n1 + n2 : 0;
	}
}

static Uint16
file_write(UxnFile *c, Uint16 addr, Uint16 len, Uint8 flags)
{
	Uint16 ret = 0;
	if(c->outside_sandbox) return 0;
@@ -258,8 +325,7 @@ file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
			c->state = FILE_WRITE;
	}
	if(c->state == FILE_WRITE) {
		if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
			ret = 0;
		ret = file_write_file(addr, len, c->f);
	}
	if (c->state == DIR_WRITE) {
		ret = dir_exists(c->current_filename);
@@ -268,38 +334,17 @@ file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
}

static Uint16
stat_fill(Uint8 *dest, Uint16 len, char c)
{
	Uint16 i;
	for (i = 0; i < len; i++)
		*(dest++) = c;
	return len;
}

static Uint16
stat_size(Uint8 *dest, Uint16 len, off_t size)
{
	Uint16 i;
	dest += len - 1;
	for (i = 0; i < len; i++) {
		*(dest--) = '0' + (Uint8)(size & 0xf);
		size = size >> 4;
	}
	return size == 0 ? len : stat_fill(dest, len, '?');
}

static Uint16
file_stat(UxnFile *c, void *dest, Uint16 len)
file_stat(UxnFile *c, Uint16 addr, Uint16 len)
{
	struct stat st;
	if(c->outside_sandbox)
		return 0;
	else if(stat(c->current_filename, &st))
		return stat_fill(dest, len, '!');
		return stat_fill(addr, len, '!');
	else if(S_ISDIR(st.st_mode))
		return stat_fill(dest, len, '-');
		return stat_fill(addr, len, '-');
	else
		return stat_size(dest, len, st.st_size);
		return stat_size(addr, len, st.st_size);
}

static Uint16
@@ -318,9 +363,7 @@ file_deo(Uint8 port)
	case 0xa5:
		addr = PEEK2(&uxn.dev[0xa4]);
		len = PEEK2(&uxn.dev[0xaa]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_stat(&uxn_file[0], &uxn.ram[addr], len);
		res = file_stat(&uxn_file[0], addr, len);
		POKE2(&uxn.dev[0xa2], res);
		break;
	case 0xa6:
@@ -329,32 +372,26 @@ file_deo(Uint8 port)
		break;
	case 0xa9:
		addr = PEEK2(&uxn.dev[0xa8]);
		res = file_init(&uxn_file[0], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
		res = file_init(&uxn_file[0], addr);
		POKE2(&uxn.dev[0xa2], res);
		break;
	case 0xad:
		addr = PEEK2(&uxn.dev[0xac]);
		len = PEEK2(&uxn.dev[0xaa]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_read(&uxn_file[0], &uxn.ram[addr], len);
		res = file_read(&uxn_file[0], addr, len);
		POKE2(&uxn.dev[0xa2], res);
		break;
	case 0xaf:
		addr = PEEK2(&uxn.dev[0xae]);
		len = PEEK2(&uxn.dev[0xaa]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_write(&uxn_file[0], &uxn.ram[addr], len, uxn.dev[0xa7]);
		res = file_write(&uxn_file[0], addr, len, uxn.dev[0xa7]);
		POKE2(&uxn.dev[0xa2], res);
		break;
	/* File 2 */
	case 0xb5:
		addr = PEEK2(&uxn.dev[0xb4]);
		len = PEEK2(&uxn.dev[0xba]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_stat(&uxn_file[1], &uxn.ram[addr], len);
		res = file_stat(&uxn_file[1], addr, len);
		POKE2(&uxn.dev[0xb2], res);
		break;
	case 0xb6:
@@ -363,23 +400,19 @@ file_deo(Uint8 port)
		break;
	case 0xb9:
		addr = PEEK2(&uxn.dev[0xb8]);
		res = file_init(&uxn_file[1], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
		res = file_init(&uxn_file[1], addr);
		POKE2(&uxn.dev[0xb2], res);
		break;
	case 0xbd:
		addr = PEEK2(&uxn.dev[0xbc]);
		len = PEEK2(&uxn.dev[0xba]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_read(&uxn_file[1], &uxn.ram[addr], len);
		res = file_read(&uxn_file[1], addr, len);
		POKE2(&uxn.dev[0xb2], res);
		break;
	case 0xbf:
		addr = PEEK2(&uxn.dev[0xbe]);
		len = PEEK2(&uxn.dev[0xba]);
		if(len > 0x10000 - addr)
			len = 0x10000 - addr;
		res = file_write(&uxn_file[1], &uxn.ram[addr], len, uxn.dev[0xb7]);
		res = file_write(&uxn_file[1], addr, len, uxn.dev[0xb7]);
		POKE2(&uxn.dev[0xb2], res);
		break;
	}
-- 
2.39.5