mirror of
https://github.com/MiSTer-devel/Main_MiSTer.git
synced 2026-04-12 03:04:02 +00:00
This allows older MS-DOS versions to see non-zero disk space remaining when free space is greater than 2GB.
1164 lines
20 KiB
C++
1164 lines
20 KiB
C++
/*
|
|
* Copyright (c) 2020, Alexey Melnikov
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <fcntl.h>
|
|
#include <sys/statvfs.h>
|
|
#include <time.h>
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "../../hardware.h"
|
|
#include "../../user_io.h"
|
|
#include "../../file_io.h"
|
|
#include "../../cfg.h"
|
|
#include "../../shmem.h"
|
|
|
|
#define SHMEM_ADDR 0x300CE000
|
|
#define SHMEM_SIZE 0x2000
|
|
static uint8_t *shmem = 0;
|
|
|
|
#define REQUEST_FLG 0
|
|
#define REQUEST_BUFFER 4
|
|
#define HDRLEN 8
|
|
#define DATA_BUFFER (REQUEST_BUFFER+66)
|
|
|
|
//#define DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define dbg_print printf
|
|
#define dbg_hexdump hexdump
|
|
#else
|
|
#define dbg_print(x,...) void()
|
|
#define dbg_hexdump(x,...) void()
|
|
#endif
|
|
|
|
enum AL_SUBFUNCTIONS {
|
|
AL_RMDIR = 0x01,
|
|
AL_MKDIR = 0x03,
|
|
AL_CHDIR = 0x05,
|
|
AL_CLOSE = 0x06,
|
|
AL_READ = 0x08,
|
|
AL_WRITE = 0x09,
|
|
AL_LOCK = 0x0A,
|
|
AL_DISKSPACE = 0x0C,
|
|
AL_SETATTR = 0x0E,
|
|
AL_GETATTR = 0x0F,
|
|
AL_RENAME = 0x11,
|
|
AL_DELETE = 0x13,
|
|
AL_OPEN = 0x16,
|
|
AL_CREATE = 0x17,
|
|
AL_FINDFIRST = 0x1B,
|
|
AL_FINDNEXT = 0x1C,
|
|
AL_SKFMEND = 0x21,
|
|
AL_QUALIFY = 0x23,
|
|
AL_SPOPEN = 0x2E,
|
|
AL_UNKNOWN = 0xFF
|
|
};
|
|
|
|
#define FAT_RO 1
|
|
#define FAT_HID 2
|
|
#define FAT_SYS 4
|
|
#define FAT_VOL 8
|
|
#define FAT_DIR 16
|
|
#define FAT_ARC 32
|
|
#define FAT_DEV 64
|
|
|
|
static char basepath[1024] = {};
|
|
static int baselen = 0;
|
|
|
|
struct dir_item_t
|
|
{
|
|
dirent64 de;
|
|
stat64 st;
|
|
};
|
|
|
|
struct lock
|
|
{
|
|
uint16_t token;
|
|
std::vector<dir_item_t> dir_items;
|
|
};
|
|
|
|
static std::map<short, lock> locks;
|
|
static short next_key = 0;
|
|
|
|
static short get_key()
|
|
{
|
|
short key;
|
|
|
|
do
|
|
{
|
|
next_key++;
|
|
if (!next_key) next_key++;
|
|
key = next_key;
|
|
|
|
} while (locks.find(key) != locks.end());
|
|
|
|
return key;
|
|
}
|
|
|
|
static short get_lock(const uint16_t token)
|
|
{
|
|
for (const auto &pair : locks)
|
|
{
|
|
if (pair.second.token == token)
|
|
{
|
|
dbg_print("! token %u has lock: %d\n", token, pair.first);
|
|
return pair.first;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static short add_lock(const uint16_t token)
|
|
{
|
|
short key = get_lock(token);
|
|
if (key)
|
|
{
|
|
locks[key].dir_items.clear();
|
|
}
|
|
else
|
|
{
|
|
key = get_key();
|
|
locks[key] = { token, {} };
|
|
dbg_print("+ add lock: %d, %u\n", key, token);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
static std::map<short, fileTYPE> open_file_handles;
|
|
static short next_fp = 1;
|
|
|
|
static short get_fp()
|
|
{
|
|
short fp;
|
|
|
|
do
|
|
{
|
|
next_fp++;
|
|
if (!next_fp) next_fp++;
|
|
fp = next_fp;
|
|
|
|
} while (open_file_handles.find(fp) != open_file_handles.end());
|
|
|
|
return fp;
|
|
}
|
|
|
|
static char* find_path(const char *name)
|
|
{
|
|
dbg_print("find_path(%s)\n", name);
|
|
|
|
static char str[1024] = {};
|
|
const char* p = strchr(name, ':');
|
|
if (p)
|
|
{
|
|
name = p + 1;
|
|
}
|
|
|
|
strcpy(str, basepath);
|
|
if (strlen(name))
|
|
{
|
|
strcat(str, "/");
|
|
strcat(str, name);
|
|
}
|
|
|
|
char *bsl;
|
|
while ((bsl = strchr(str, '\\'))) *bsl = '/';
|
|
|
|
dbg_print("Requested path: %s\n", str);
|
|
|
|
if (strncmp(basepath, str, baselen))
|
|
{
|
|
dbg_print("Not belonging to shared folder\n");
|
|
str[0] = 0;
|
|
}
|
|
else if (str[baselen] && str[baselen] != '/')
|
|
{
|
|
dbg_print("No / after root\n");
|
|
str[0] = 0;
|
|
}
|
|
else if (str[baselen])
|
|
{
|
|
char *cur = str + baselen;
|
|
while (*cur)
|
|
{
|
|
cur++;
|
|
char *next = strchr(cur, '/');
|
|
if (!next) next = cur + strlen(cur);
|
|
int len = next - cur;
|
|
|
|
if (!len && !*next) break;
|
|
|
|
if (!len || !strncmp(cur, ".", len))
|
|
{
|
|
strcpy(cur, next + 1);
|
|
cur--;
|
|
continue;
|
|
}
|
|
|
|
if (!strncmp(cur, "..", len))
|
|
{
|
|
cur -= 2;
|
|
while (*cur != '/') cur--;
|
|
|
|
if (cur < str + baselen)
|
|
{
|
|
printf("Going above root\n");
|
|
str[0] = 0;
|
|
break;
|
|
}
|
|
|
|
// collapse the component
|
|
strcpy(cur, next);
|
|
continue;
|
|
}
|
|
|
|
cur = next;
|
|
}
|
|
}
|
|
|
|
// remove trailing /
|
|
int len = strlen(str);
|
|
if (len && str[len - 1] == '/') str[len - 1] = 0;
|
|
|
|
dbg_print("Converted path: %s\n", str);
|
|
|
|
if (str[0])
|
|
{
|
|
char *p = strrchr(str, '/');
|
|
if (!p) str[0] = 0;
|
|
else
|
|
{
|
|
*p = 0;
|
|
if (!PathIsDir(str, 0)) str[0] = 0;
|
|
else *p = '/';
|
|
}
|
|
}
|
|
|
|
dbg_print("returned path: %s\n", str);
|
|
return str;
|
|
}
|
|
|
|
static void __attribute__((noinline)) memcpyb(void *dst, const void *src, int len)
|
|
{
|
|
char *d = (char*)dst;
|
|
char *s = (char*)src;
|
|
while (len--) *d++ = *s++;
|
|
}
|
|
|
|
|
|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Directory Attribute Flags
|
|
// | | | | | | | \-- 1 = read only
|
|
// | | | | | | \--- 1 = hidden
|
|
// | | | | | \---- 1 = system
|
|
// | | | | \----- 1 = volume label(exclusive)
|
|
// | | | \------ 1 = subdirectory
|
|
// | | \------- 1 = archive
|
|
// \---+-------unused
|
|
|
|
static uint32_t get_attr(char *path, uint16_t *time, uint16_t *date, uint32_t *size)
|
|
{
|
|
if (time) *time = 0;
|
|
if (date) *date = 0;
|
|
if (size) *size = 0;
|
|
|
|
stat64 *st = getPathStat(path);
|
|
if (!st) return 0;
|
|
|
|
tm *t = localtime(&st->st_mtime);
|
|
if (time) *time = (t->tm_sec / 2) | (t->tm_min << 5) | (t->tm_hour << 11);
|
|
if (date) *date = t->tm_mday | ((t->tm_mon + 1) << 5) | ((t->tm_year - 80) << 9);
|
|
if (size) *size = st->st_size;
|
|
return st->st_mode;
|
|
}
|
|
|
|
static void name83(const char *src, char *dst)
|
|
{
|
|
int namelen = 0;
|
|
int extlen = 0;
|
|
|
|
const char *p = strrchr(src, '/');
|
|
if (p) src = p + 1;
|
|
|
|
if (!strcmp(src, ".") || !strcmp(src, ".."))
|
|
{
|
|
namelen = strlen(src);
|
|
}
|
|
else
|
|
{
|
|
p = strrchr(src, '.');
|
|
if (!p) namelen = strlen(src);
|
|
else
|
|
{
|
|
namelen = p - src;
|
|
extlen = strlen(src) - namelen - 1;
|
|
}
|
|
}
|
|
|
|
|
|
char ext[4] = { ' ', ' ', ' ', 0 };
|
|
if (p) memcpy(ext, p + 1, extlen);
|
|
for (int i = 0; i < namelen; i++) dst[i] = toupper(src[i]);
|
|
while (namelen < 8) dst[namelen++] = ' ';
|
|
for (int i = 0; i < 3; i++) dst[8 + i] = toupper(ext[i]);
|
|
}
|
|
|
|
static int cmp_name(const char *name, const char *flt)
|
|
{
|
|
int namelen = 0;
|
|
int extlen = 0;
|
|
|
|
const char *ext = strrchr(name, '.');
|
|
if (!ext)
|
|
{
|
|
namelen = strlen(name);
|
|
ext = name + namelen;
|
|
}
|
|
else
|
|
{
|
|
namelen = ext - name;
|
|
ext++;
|
|
extlen = strlen(ext);
|
|
}
|
|
|
|
if (namelen > 8 || extlen > 3) return 0;
|
|
|
|
char testname[16];
|
|
char fltname[16];
|
|
name83(name, testname);
|
|
name83(flt, fltname);
|
|
|
|
testname[11] = 0;
|
|
fltname[11] = 0;
|
|
|
|
char *cmpname = fltname;
|
|
char *cmpend = fltname + 8;
|
|
char *cur = testname;
|
|
|
|
while (cmpname < cmpend)
|
|
{
|
|
if (*cmpname == '?')
|
|
{
|
|
cmpname++;
|
|
cur++;
|
|
}
|
|
else if (*cmpname == '*')
|
|
{
|
|
break;
|
|
}
|
|
else if (*cmpname++ != *cur++)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
cmpname = fltname + 8;
|
|
cmpend = fltname + 11;
|
|
cur = testname + 8;
|
|
|
|
while (cmpname < cmpend)
|
|
{
|
|
if (*cmpname == '?')
|
|
{
|
|
cmpname++;
|
|
cur++;
|
|
}
|
|
else if (*cmpname == '*')
|
|
{
|
|
break;
|
|
}
|
|
else if (*cmpname++ != *cur++)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int process_request(void *reqres_buffer)
|
|
{
|
|
static char str[1024];
|
|
int len = *(unsigned short*)reqres_buffer;
|
|
char func = ((char*)reqres_buffer)[4];
|
|
|
|
dbg_hexdump(reqres_buffer, 8, 0);
|
|
if(func != AL_WRITE) dbg_hexdump((char*)reqres_buffer+8, len, 8);
|
|
|
|
short res = -1;
|
|
short reslen = 0;
|
|
|
|
unsigned short idx = 0;
|
|
short key = 0;
|
|
|
|
if (!baselen)
|
|
{
|
|
if (strlen(cfg.shared_folder))
|
|
{
|
|
if (cfg.shared_folder[0] == '/') strcpy(basepath, cfg.shared_folder);
|
|
else
|
|
{
|
|
strcpy(basepath, HomeDir());
|
|
strcat(basepath, "/");
|
|
strcat(basepath, cfg.shared_folder);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcpy(basepath, HomeDir());
|
|
strcat(basepath, "/shared");
|
|
}
|
|
|
|
baselen = strlen(basepath);
|
|
if (baselen && basepath[baselen - 1] == '/')
|
|
{
|
|
basepath[baselen - 1] = 0;
|
|
baselen--;
|
|
}
|
|
|
|
if (baselen) FileCreatePath(basepath);
|
|
}
|
|
|
|
char *buf = ((char*)reqres_buffer) + 8;
|
|
buf[len] = 0;
|
|
|
|
switch (func)
|
|
{
|
|
case AL_RMDIR:
|
|
{
|
|
dbg_print("> AL_RMDIR\n");
|
|
|
|
char *path = find_path(buf);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
if(!DirDelete(path))
|
|
{
|
|
dbg_print("Cannot delete dir %s\n", path);
|
|
res = 29;
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_MKDIR:
|
|
{
|
|
dbg_print("> AL_MKDIR\n");
|
|
|
|
char *path = find_path(buf);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
if (!FileCreatePath(path))
|
|
{
|
|
res = 29;
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_CHDIR:
|
|
{
|
|
dbg_print("> AL_CHDIR\n");
|
|
|
|
char *path = find_path(buf);
|
|
if (!*path || !PathIsDir(path))
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_OPEN:
|
|
{
|
|
dbg_print("> AL_OPEN\n");
|
|
|
|
uint16_t attr = *(uint16_t *)buf;
|
|
char *path = find_path(buf + 6);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
if (!FileExists(path, 0))
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
int mode = attr & 3;
|
|
|
|
short key = get_fp();
|
|
open_file_handles[key] = {};
|
|
|
|
if (!FileOpenEx(&open_file_handles[key], path, mode, 0, 0))
|
|
{
|
|
open_file_handles.erase(key);
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
dbg_print("opened handle: %d\n", key);
|
|
|
|
*buf++ = 0;
|
|
name83(path, buf);
|
|
buf += 11;
|
|
get_attr(path, (uint16_t*)buf, (uint16_t*)(buf + 2), (uint32_t*)(buf + 4));
|
|
buf += 8;
|
|
*buf++ = key;
|
|
*buf++ = key >> 8;
|
|
*buf++ = 0;
|
|
*buf++ = 0;
|
|
*buf++ = mode;
|
|
|
|
/*
|
|
Bit 0-2 access mode
|
|
000=read
|
|
001=write
|
|
010=read/write
|
|
4-6 sharing mode
|
|
000=compatibility
|
|
001=deny read/write
|
|
010=deny write
|
|
011=deny read
|
|
100=deny none
|
|
13 critical error handling
|
|
0=execute INT 24
|
|
1=return error code
|
|
14 buffering
|
|
0=buffer writes
|
|
1=don't buffer writes
|
|
15 1=FCB SFT
|
|
*/
|
|
reslen = 25;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_CREATE:
|
|
{
|
|
dbg_print("> AL_CREATE\n");
|
|
|
|
char *path = find_path(buf + 6);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
int mode = O_RDWR | O_CREAT | O_TRUNC;
|
|
|
|
short key = get_fp();
|
|
open_file_handles[key] = {};
|
|
|
|
if (!FileOpenEx(&open_file_handles[key], path, mode, 0, 0))
|
|
{
|
|
open_file_handles.erase(key);
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
dbg_print("opened handle: %d\n", key);
|
|
|
|
*buf++ = 0;
|
|
name83(path, buf);
|
|
buf += 11;
|
|
get_attr(path, (uint16_t*)buf, (uint16_t*)(buf + 2), (uint32_t*)(buf + 4));
|
|
buf += 8;
|
|
*buf++ = key;
|
|
*buf++ = key >> 8;
|
|
*buf++ = 0;
|
|
*buf++ = 0;
|
|
*buf++ = 2;
|
|
|
|
reslen = 25;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_SPOPEN:
|
|
{
|
|
dbg_print("> AL_SPOPEN\n");
|
|
|
|
/* actioncode contains instructions about how to behave...
|
|
* high nibble = action if file does NOT exist:
|
|
* 0000 fail
|
|
* 0001 create
|
|
* low nibble = action if file DOES exist
|
|
* 0000 fail
|
|
* 0001 open
|
|
* 0010 truncate/open */
|
|
uint16_t attr = *(uint16_t *)buf;
|
|
uint16_t actioncode = *(uint16_t *)(buf + 2);
|
|
uint16_t openmode = *(uint16_t *)(buf + 4);
|
|
|
|
char *path = find_path(buf + 6);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
int mode = openmode & 0x3;
|
|
uint16_t spopres = 0;
|
|
|
|
if (FileExists(path, 0))
|
|
{
|
|
if ((actioncode & 0xF) == 1)
|
|
{
|
|
spopres = 1;
|
|
}
|
|
else if ((actioncode & 0xF) == 2)
|
|
{
|
|
mode = O_RDWR | O_TRUNC;
|
|
spopres = 3;
|
|
}
|
|
else
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// some copiers create an empty file with hidden attribute and then fail if found non-hidden file
|
|
// so prohibit to create a hidden file.
|
|
if ((actioncode & 0xF0) == 0x10 && !(attr & FAT_HID))
|
|
{
|
|
mode = O_RDWR | O_CREAT;
|
|
spopres = 2;
|
|
}
|
|
else
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
key = get_fp();
|
|
open_file_handles[key] = {};
|
|
|
|
if (!FileOpenEx(&open_file_handles[key], path, mode, 0, 0))
|
|
{
|
|
open_file_handles.erase(key);
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
dbg_print("opened handle: %d\n", key);
|
|
|
|
*buf++ = 0;
|
|
name83(path, buf);
|
|
buf += 11;
|
|
get_attr(path, (uint16_t*)buf, (uint16_t*)(buf + 2), (uint32_t*)(buf + 4)); // 12 14 16
|
|
buf += 8;
|
|
*buf++ = key;
|
|
*buf++ = key >> 8;
|
|
*buf++ = spopres;
|
|
*buf++ = spopres >> 8;
|
|
*buf++ = openmode & 0x7f;
|
|
|
|
reslen = 25;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_CLOSE:
|
|
{
|
|
dbg_print("> AL_CLOSE\n");
|
|
|
|
key = *(short *)buf;
|
|
if (open_file_handles.find(key) != open_file_handles.end())
|
|
{
|
|
FileClose(&open_file_handles[key]);
|
|
open_file_handles.erase(key);
|
|
|
|
dbg_print("closed handle: %d\n", key);
|
|
}
|
|
|
|
reslen = 0;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_READ:
|
|
{
|
|
dbg_print("> AL_READ\n");
|
|
|
|
key = buf[4] | (buf[5] << 8);
|
|
if (open_file_handles.find(key) == open_file_handles.end())
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
uint32_t off;
|
|
memcpyb(&off, buf, 4);
|
|
uint16_t sz = buf[6] | (buf[7] << 8);
|
|
dbg_print(" read %d bytes at %d\n", sz, off);
|
|
|
|
FileSeek(&open_file_handles[key], off, SEEK_SET);
|
|
|
|
int read = FileReadAdv(&open_file_handles[key], buf, sz, -1);
|
|
if (read < 0)
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
dbg_print(" was read %d\n", read);
|
|
|
|
reslen = read;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_WRITE:
|
|
{
|
|
dbg_print("> AL_WRITE\n");
|
|
|
|
key = buf[4] | (buf[5] << 8);
|
|
if (open_file_handles.find(key) == open_file_handles.end())
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
uint32_t off;
|
|
memcpyb(&off, buf, 4);
|
|
uint16_t sz = buf[6] | (buf[7] << 8);
|
|
dbg_print(" write %d bytes at %d\n", sz, off);
|
|
|
|
FileSeek(&open_file_handles[key], off, SEEK_SET);
|
|
|
|
int written = 0;
|
|
if (sz)
|
|
{
|
|
written = FileWriteAdv(&open_file_handles[key], buf + 8, sz);
|
|
if (!written)
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
dbg_print(" written %d\n", written);
|
|
|
|
*buf++ = written;
|
|
*buf++ = written >> 8;
|
|
|
|
reslen = 2;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_LOCK:
|
|
{
|
|
dbg_print("> AL_LOCK\n");
|
|
|
|
reslen = 0;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_DISKSPACE:
|
|
{
|
|
dbg_print("> AL_DISKSPACE\n");
|
|
uint32_t total = 10;
|
|
uint32_t avail = 1;
|
|
|
|
// Set the sector size to the maximum size supported by FAT16:
|
|
// 1024 MB - 2047 MB: 64 Sectors/Cluster, 32K Cluster Size
|
|
//
|
|
// This allows for MS-DOS version that only support up to 64
|
|
// Sectors/Cluster to display a non-zero value for disk space remaning.
|
|
uint32_t spc = 64; // Sectors/Cluster (normally based on partition size)
|
|
uint32_t bps = 512; // Bytes/Sector (this value is not adjusted)
|
|
|
|
struct statvfs st;
|
|
if (!statvfs(getFullPath(basepath), &st))
|
|
{
|
|
// Calculate the size available and remaining as reported from Linux
|
|
uint64_t sz = st.f_bsize * st.f_blocks;
|
|
uint64_t av = st.f_bsize * st.f_bavail;
|
|
// Convert those values into cluster sizes.
|
|
total = sz / (bps * spc);
|
|
avail = av / (bps * spc);
|
|
}
|
|
|
|
// Limit the total and available sizes to the maximum supported size
|
|
// based on the cluster size. (In our case 2GB at 32KB clusters)
|
|
if (total > UINT16_MAX) total = UINT16_MAX;
|
|
if (avail > UINT16_MAX) avail = UINT16_MAX;
|
|
|
|
*buf++ = total;
|
|
*buf++ = total >> 8;
|
|
*buf++ = bps;
|
|
*buf++ = bps >> 8;
|
|
*buf++ = avail;
|
|
*buf++ = avail >> 8;
|
|
|
|
reslen = 6;
|
|
res = spc;
|
|
}
|
|
break;
|
|
|
|
case AL_SETATTR:
|
|
{
|
|
dbg_print("> AL_SETATTR\n");
|
|
reslen = 0;
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_GETATTR:
|
|
{
|
|
dbg_print("> AL_GETATTR\n");
|
|
|
|
char *path = find_path(buf);
|
|
if (!*path)
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
uint16_t time, date;
|
|
uint32_t sz;
|
|
uint32_t mode = get_attr(path, &time, &date, &sz);
|
|
|
|
if (!mode)
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
*buf++ = time;
|
|
*buf++ = time >> 8;
|
|
*buf++ = date;
|
|
*buf++ = date >> 8;
|
|
*buf++ = sz;
|
|
*buf++ = sz >> 8;
|
|
*buf++ = sz >> 16;
|
|
*buf++ = sz >> 24;
|
|
*buf++ = (mode & S_IFDIR) ? FAT_DIR : 0;
|
|
*buf++ = 0;
|
|
|
|
res = 0;
|
|
reslen = 10;
|
|
}
|
|
break;
|
|
|
|
case AL_RENAME:
|
|
{
|
|
dbg_print("> AL_RENAME\n");
|
|
|
|
int srclen = (*buf++) & 0xFF;
|
|
char *path = find_path(buf + srclen);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
strcpy(str, getFullPath(path));
|
|
|
|
buf[srclen] = 0;
|
|
path = find_path(buf);
|
|
if (!*path)
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
if (rename(getFullPath(path), str))
|
|
{
|
|
res = 5;
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_DELETE:
|
|
{
|
|
dbg_print("> AL_DELETE\n");
|
|
|
|
char *path = find_path(buf);
|
|
if (!*path)
|
|
{
|
|
res = 3;
|
|
break;
|
|
}
|
|
|
|
if(strchr(path, '?') || strchr(path, '*'))
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
if (!FileDelete(path))
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
break;
|
|
|
|
case AL_FINDFIRST:
|
|
{
|
|
dbg_print("> AL_FINDFIRST\n");
|
|
|
|
const uint16_t token = ((uint16_t *)buf)[0];
|
|
|
|
char attr = buf[2];
|
|
|
|
char *path = find_path(buf+3);
|
|
if (!*path)
|
|
{
|
|
res = 0x12;
|
|
break;
|
|
}
|
|
|
|
char *flt = strrchr(path, '/');
|
|
if (!*flt)
|
|
{
|
|
res = 0x12;
|
|
break;
|
|
}
|
|
|
|
*flt++ = 0;
|
|
key = add_lock(token);
|
|
|
|
const char* full_path = getFullPath(path);
|
|
DIR *d = opendir(full_path);
|
|
if (!d)
|
|
{
|
|
locks.erase(key);
|
|
printf("Couldn't open dir: %s\n", full_path);
|
|
res = 0x12;
|
|
break;
|
|
}
|
|
|
|
if (attr == 8)
|
|
{
|
|
struct dirent64 de = {};
|
|
strcpy(de.d_name, "MiSTer");
|
|
locks[key].dir_items.push_back({ de, {} });
|
|
|
|
*buf++ = 8;
|
|
memcpyb(buf, "MiSTer ", 11);
|
|
|
|
buf += 11;
|
|
*buf++ = 0; *buf++ = 0; // time;
|
|
*buf++ = 0; *buf++ = 0; // date;
|
|
*buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; // size;
|
|
*buf++ = key;
|
|
*buf++ = key >> 8;
|
|
*buf++ = 0; *buf++ = 0;
|
|
|
|
res = 0;
|
|
reslen = 24;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
struct dirent64 de, *de2;
|
|
while ((de2 = readdir64(d)))
|
|
{
|
|
if (de2->d_type == DT_REG || (attr & FAT_DIR))
|
|
{
|
|
memcpy(&de, de2, sizeof(dirent64));
|
|
sprintf(str, "%s/%s", path, de.d_name);
|
|
stat64 *st = getPathStat(str);
|
|
|
|
if (st && cmp_name(de.d_name, flt))
|
|
{
|
|
name83(de2->d_name, de.d_name);
|
|
de.d_name[11] = 0;
|
|
locks[key].dir_items.push_back({ de, *st });
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
}
|
|
// fall through
|
|
|
|
case AL_FINDNEXT:
|
|
{
|
|
if (func == AL_FINDNEXT)
|
|
{
|
|
dbg_print("> AL_FINDNEXT\n");
|
|
|
|
key = *(short *)buf;
|
|
idx = *(short *)(buf+2);
|
|
idx++;
|
|
|
|
if (locks.find(key) == locks.end())
|
|
{
|
|
dbg_print("Key %d not found\n", key);
|
|
res = 0x12;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idx >= locks[key].dir_items.size())
|
|
{
|
|
if (locks.find(key) != locks.end()) locks.erase(key);
|
|
|
|
dbg_print("No more items\n");
|
|
res = 0x12;
|
|
break;
|
|
}
|
|
|
|
*buf++ = (locks[key].dir_items[idx].de.d_type == DT_DIR) ? FAT_DIR : 0;
|
|
memcpyb(buf, locks[key].dir_items[idx].de.d_name, 11);
|
|
buf += 11;
|
|
|
|
tm *t = localtime(&locks[key].dir_items[idx].st.st_mtime);
|
|
uint16_t time = (t->tm_sec / 2) | (t->tm_min << 5) | (t->tm_hour << 11);
|
|
uint16_t date = t->tm_mday | ((t->tm_mon + 1) << 5) | ((t->tm_year - 80) << 9);
|
|
|
|
*buf++ = time;
|
|
*buf++ = time >> 8;
|
|
*buf++ = date;
|
|
*buf++ = date >> 8;
|
|
|
|
memcpyb(buf, &locks[key].dir_items[idx].st.st_size, 4);
|
|
buf += 4;
|
|
*buf++ = key;
|
|
*buf++ = key >> 8;
|
|
*buf++ = idx;
|
|
*buf++ = idx >> 8;
|
|
|
|
res = 0;
|
|
reslen = 24;
|
|
}
|
|
break;
|
|
|
|
case AL_SKFMEND:
|
|
{
|
|
dbg_print("> AL_SKFMEND\n");
|
|
|
|
key = buf[4] | (buf[5] << 8);
|
|
if (open_file_handles.find(key) == open_file_handles.end())
|
|
{
|
|
res = 2;
|
|
break;
|
|
}
|
|
|
|
int32_t off;
|
|
memcpyb(&off, buf, 4);
|
|
|
|
int32_t sz = open_file_handles[key].size;
|
|
off += sz;
|
|
if (off < 0) off = 0;
|
|
|
|
memcpyb(buf, &off, 4);
|
|
|
|
res = 0;
|
|
reslen = 4;
|
|
}
|
|
break;
|
|
|
|
case AL_QUALIFY:
|
|
{
|
|
dbg_print("> AL_QUALIFY\n");
|
|
|
|
res = 0;
|
|
reslen = 128;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
((short *)reqres_buffer)[0] = reslen;
|
|
((short *)reqres_buffer)[2] = res;
|
|
dbg_print("result %d, %d:\n", reslen, res);
|
|
dbg_hexdump(reqres_buffer, 8, 0);
|
|
if (reslen > 0 && func != AL_READ)
|
|
{
|
|
dbg_hexdump((char*)reqres_buffer + 8, reslen, 8);
|
|
}
|
|
return reslen;
|
|
}
|
|
|
|
void x86_share_poll()
|
|
{
|
|
if (!shmem)
|
|
{
|
|
shmem = (uint8_t *)shmem_map(SHMEM_ADDR, SHMEM_SIZE);
|
|
if (!shmem) shmem = (uint8_t *)-1;
|
|
}
|
|
else if (shmem != (uint8_t *)-1)
|
|
{
|
|
static uint32_t old_req_id = 0;
|
|
uint32_t req_id = *(uint32_t*)(shmem + REQUEST_FLG);
|
|
|
|
if ((uint16_t)old_req_id != (uint16_t)req_id)
|
|
{
|
|
dbg_print("\nnew req: %08X\n", req_id);
|
|
old_req_id = req_id;
|
|
|
|
if (((req_id >> 16) & 0xFFFF) == 0xA55A && ((req_id + 77) & 0xFF) == ((req_id >> 8) & 0xFF))
|
|
{
|
|
process_request(shmem + REQUEST_BUFFER);
|
|
*(uint16_t*)(shmem + REQUEST_FLG + 2) = (uint16_t)req_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void x86_share_reset()
|
|
{
|
|
open_file_handles.clear();
|
|
locks.clear();
|
|
next_fp = 1;
|
|
next_key = 1;
|
|
}
|