aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-12 22:38:20 +0200
committerJavier <dev.git@javispedro.com>2022-04-12 22:38:20 +0200
commita109a1193ca3cf64a29265103075922d2dd9fc1b (patch)
treecde59f7e29ccca176dd150a51e8caf4613a86d1f
parent2b9f509a04acb88d9ac72bce23b28099efdc4878 (diff)
downloadvbados-a109a1193ca3cf64a29265103075922d2dd9fc1b.tar.gz
vbados-a109a1193ca3cf64a29265103075922d2dd9fc1b.zip
add file date/time listing support, closeall call
-rw-r--r--dostsr.h24
-rw-r--r--int21dos.h6
-rw-r--r--mousmain.c4
-rw-r--r--sfmain.c83
-rw-r--r--sftsr.c373
-rw-r--r--sftsr.h32
-rw-r--r--unixtime.h130
-rw-r--r--vbox.h3
-rw-r--r--vboxdev.h12
-rw-r--r--vboxshfl.h31
10 files changed, 542 insertions, 156 deletions
diff --git a/dostsr.h b/dostsr.h
index 726ef3a..34e5a57 100644
--- a/dostsr.h
+++ b/dostsr.h
@@ -76,37 +76,35 @@ static __segment allocate_umb(unsigned size)
return new_segment;
}
-static __segment reallocate_to_umb(segment_t __far * cur_seg, unsigned segment_size)
+static __segment reallocate_to_umb(segment_t cur_seg, unsigned segment_size)
{
- segment_t old_psp_segment = *cur_seg - (DOS_PSP_SIZE/16);
- segment_t new_psp_segment;
+ segment_t old_segment_psp = cur_seg - (DOS_PSP_SIZE/16);
+ segment_t new_segment_psp;
deallocate_environment(_psp);
// If we are already in UMA, don't bother
- if (old_psp_segment >= 0xA000) {
+ if (old_segment_psp >= 0xA000) {
return 0;
}
- new_psp_segment = allocate_umb(segment_size);
+ new_segment_psp = allocate_umb(segment_size);
- if (new_psp_segment && new_psp_segment >= 0xA000) {
- segment_t new_segment = new_psp_segment + (DOS_PSP_SIZE/16);
- printf("Moving to upper memory\n");
+ if (new_segment_psp && new_segment_psp >= 0xA000) {
+ segment_t new_segment = new_segment_psp + (DOS_PSP_SIZE/16);
// Create a new program instance including PSP at the new_segment
- copy_program(new_psp_segment, old_psp_segment, segment_size);
+ copy_program(new_segment_psp, old_segment_psp, segment_size);
// Tell DOS to "switch" to the new program
- dos_set_psp(new_psp_segment);
+ dos_set_psp(new_segment_psp);
// Return the new segment
return new_segment;
} else {
- printf("No upper memory available\n");
- if (new_psp_segment) {
+ if (new_segment_psp) {
// In case we got another low-memory segment...
- dos_free(new_psp_segment);
+ dos_free(new_segment_psp);
}
return 0;
diff --git a/int21dos.h b/int21dos.h
index c41ce44..f414216 100644
--- a/int21dos.h
+++ b/int21dos.h
@@ -46,7 +46,7 @@ enum dos_error {
DOS_ERROR_OUT_OF_MEMORY = 14,
DOS_ERROR_INVALID_DRIVE = 15,
DOS_ERROR_CURRENT_DIRECTORY = 16,
- DOS_ERROR_OT_SAME_DEVICE = 17,
+ DOS_ERROR_NOT_SAME_DEVICE = 17,
DOS_ERROR_NO_MORE_FILES = 18,
DOS_ERROR_WRITE_PROTECT = 19,
DOS_ERROR_BAD_UNIT = 20,
@@ -62,6 +62,9 @@ enum dos_error {
DOS_ERROR_HANDLE_EOF = 38,
DOS_ERROR_HANDLE_DISK_FULL = 39,
+
+ DOS_ERROR_FILE_EXISTS = 80,
+ DOS_ERROR_CANNOT_MAKE = 82,
};
enum dos_allocation_strategy {
@@ -316,6 +319,7 @@ enum DOS_REDIR_SUBFUNCTION {
DOS_FN_PRINTSETUP = 0x1F,
DOS_FN_FLUSH = 0x20,
DOS_FN_SEEK_END = 0x21,
+ DOS_FN_ATEXIT = 0x22,
DOS_FN_QUALIFY = 0x23,
DOS_FN_OPEN_EX = 0x2E
};
diff --git a/mousmain.c b/mousmain.c
index 8692048..c6d2e96 100644
--- a/mousmain.c
+++ b/mousmain.c
@@ -274,8 +274,8 @@ static int configure_driver(LPTSRDATA data)
static int move_driver_to_umb(LPTSRDATA __far * data)
{
- segment_t cur_seg = FP_SEG(data);
- segment_t umb_seg = reallocate_to_umb(&cur_seg, get_resident_size() + DOS_PSP_SIZE);
+ segment_t cur_seg = FP_SEG(*data);
+ segment_t umb_seg = reallocate_to_umb(cur_seg, get_resident_size() + DOS_PSP_SIZE);
if (umb_seg) {
// Update the data pointer with the new segment
diff --git a/sfmain.c b/sfmain.c
index 6911bcb..32a48b4 100644
--- a/sfmain.c
+++ b/sfmain.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <dos.h>
#include "dlog.h"
@@ -61,6 +62,23 @@ static int list_folders(LPTSRDATA data)
unsigned num_maps = sizeof(maps) / sizeof(SHFLMAPPING);
unsigned i;
+ printf("Mounted drives:\n");
+
+ for (i = 0; i < NUM_DRIVES; i++) {
+ if (data->drives[i].root == SHFL_ROOT_NIL) {
+ // Not mounted
+ continue;
+ }
+
+ err = vbox_shfl_query_map_name(&data->vb, data->hgcm_client_id, data->drives[i].root, &str.shflstr);
+ if (err) {
+ printf("Error on Query Map Name, err=%ld\n", err);
+ continue;
+ }
+
+ printf(" %s on %c:\n", str.buf, drive_index_to_letter(i));
+ }
+
err = vbox_shfl_query_mappings(&data->vb, data->hgcm_client_id, 0, &num_maps, maps);
if (err) {
printf("Error on Query Mappings, err=%ld\n", err);
@@ -82,14 +100,46 @@ static int list_folders(LPTSRDATA data)
return 0;
}
+/** Closes all currently open files.
+ * @param drive drive number, or -1 to close files from all drives. */
+static void close_openfiles(LPTSRDATA data, int drive)
+{
+ SHFLROOT search_root = SHFL_ROOT_NIL;
+ int32_t err;
+ unsigned i;
+
+ if (drive >= 0) {
+ search_root = data->drives[drive].root;
+ }
+
+ for (i = 0; i < NUM_FILES; i++) {
+ if (data->files[i].root == SHFL_ROOT_NIL) {
+ // Already closed
+ continue;
+ }
+ if (search_root != SHFL_ROOT_NIL &&
+ data->files[i].root != search_root) {
+ // File from a different shared folder
+ continue;
+ }
+
+ err = vbox_shfl_close(&data->vb, data->hgcm_client_id, data->files[i].root, data->files[i].handle);
+ if (err) {
+ printf("Error on Close File, err=%ld\n", err);
+ // Ignore it
+ }
+
+ data->files[i].root = SHFL_ROOT_NIL;
+ data->files[i].handle = SHFL_HANDLE_NIL;
+ }
+}
+
static int mount_shfl(LPTSRDATA data, int drive, const char *folder)
{
int32_t err;
SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN);
SHFLROOT root = SHFL_ROOT_NIL;
- printf("Mounting %s...\n", folder);
-
shflstring_strcpy(&str.shflstr, folder);
err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &str.shflstr, &root);
@@ -107,6 +157,8 @@ static int unmount_shfl(LPTSRDATA data, int drive)
{
int32_t err;
+ close_openfiles(data, drive);
+
err = vbox_shfl_unmap_folder(&data->vb, data->hgcm_client_id,
data->drives[drive].root);
if (err) {
@@ -252,6 +304,10 @@ static int unmount_all(LPTSRDATA data)
int err = 0;
unsigned i;
+ // Close all files
+ close_openfiles(data, -1);
+
+ // Unmount all drives
for (i = 0 ; i < NUM_DRIVES; i++) {
if (data->drives[i].root != SHFL_ROOT_NIL) {
if (unmount(data, drive_index_to_letter(i)) != 0) {
@@ -272,10 +328,25 @@ static int configure_driver(LPTSRDATA data)
for (i = 0; i < NUM_DRIVES; ++i) {
data->drives[i].root = SHFL_ROOT_NIL;
}
+ for (i = 0; i < NUM_FILES; ++i) {
+ data->files[i].root = SHFL_ROOT_NIL;
+ data->files[i].handle = SHFL_HANDLE_NIL;
+ }
+ // Initialize TSR data
data->dossda = dos_get_swappable_dos_area();
- data->search_dir_handle = SHFL_HANDLE_NIL;
+ // Get the current timezone offset
+ if (getenv("TZ")) {
+ tzset();
+ printf("Using timezone from TZ variable (%s)\n", tzname[0]);
+ data->tz_offset = timezone / 2;
+ } else {
+ printf("No TZ environment variable\n");
+ data->tz_offset = 0;
+ }
+
+ // Now try to initialize VirtualBox communication
err = vbox_init_device(&data->vb);
if (err) {
fprintf(stderr, "Cannot find VirtualBox PCI device, err=%ld\n", err);
@@ -315,8 +386,8 @@ static int configure_driver(LPTSRDATA data)
static int move_driver_to_umb(LPTSRDATA __far * data)
{
- segment_t cur_seg = FP_SEG(data);
- segment_t umb_seg = reallocate_to_umb(&cur_seg, get_resident_size() + DOS_PSP_SIZE);
+ segment_t cur_seg = FP_SEG(*data);
+ segment_t umb_seg = reallocate_to_umb(cur_seg, get_resident_size() + DOS_PSP_SIZE);
if (umb_seg) {
// Update the data pointer with the new segment
@@ -376,6 +447,8 @@ static int unconfigure_driver(LPTSRDATA data)
vbox_hgcm_disconnect(&data->vb, data->hgcm_client_id);
data->hgcm_client_id = 0;
+ vbox_release_buffer(&data->vb);
+
return 0;
}
diff --git a/sftsr.c b/sftsr.c
index 40eda4b..a5846c2 100644
--- a/sftsr.c
+++ b/sftsr.c
@@ -22,6 +22,7 @@
#include <i86.h>
#include "int21dos.h"
+#include "unixtime.h"
#include "vboxshfl.h"
#include "sftsr.h"
@@ -34,44 +35,6 @@ static SHFLDIRINFO_WITH_BUF(shfldirinfo, SHFL_MAX_LEN);
static SHFLCREATEPARMS createparms;
-union qword_to_words {
- uint64_t q;
- struct {
- uint16_t w[4];
- };
-};
-
-#if 0
-static uint32_t map_timestampns_to_timestamps(uint64_t timestampns)
-{
- unsigned long timestampsec;
-
- __asm {
- push eax ; Preserve 32-bit regs
- push edx
- mov eax, dword ptr [timestampns]
- mov edx, dword ptr [timestampns + 4]
-
- mov ecx, 1000000000 /* ns to seconds */
- div ecx
- mov [timestampsec], eax
- pop edx
- pop eax
- }
-
- return timestampsec;
-}
-#endif
-
-static void map_timestampns_to_dostime(uint64_t timestampns,
- uint16_t __far * time, uint16_t __far * date)
-{
- // TODO
- (void) timestampns;
- *time = 0;
- *date = 0;
-}
-
static uint8_t map_shfl_attr_to_dosattr(const SHFLFSOBJATTR *a)
{
// DOS attributes are in the higher word of fMode (see RTFS_DOS_*)
@@ -84,55 +47,17 @@ static void map_shfl_info_to_dossft(DOSSFT __far *sft, SHFLFSOBJINFO *i)
{
sft->attr = map_shfl_attr_to_dosattr(&i->Attr);
sft->f_size = i->cbObject;
- map_timestampns_to_dostime(i->ModificationTime, &sft->f_time, &sft->f_date);
+ timestampns_to_dos_time(&sft->f_time, &sft->f_date, i->ModificationTime, data.tz_offset);
}
static void map_shfl_info_to_dosdir(DOSDIR __far *dir, SHFLFSOBJINFO *i)
{
dir->attr = map_shfl_attr_to_dosattr(&i->Attr);
dir->f_size = i->cbObject;
- map_timestampns_to_dostime(i->ModificationTime, &dir->f_time, &dir->f_date);
+ timestampns_to_dos_time(&dir->f_time, &dir->f_date, i->ModificationTime, data.tz_offset);
dir->start_cluster = 0;
}
-static uint64_t get_sft_vbox_handle(DOSSFT __far *sft)
-{
- union qword_to_words u;
- u.w[0] = sft->start_cluster;
- u.w[1] = sft->last_rel_cluster;
- u.w[2] = sft->last_abs_cluster;
- u.w[3] = sft->dir_sector;
- return u.q;
-}
-
-static void set_sft_vbox_handle(DOSSFT __far *sft, uint64_t handle)
-{
- union qword_to_words u;
- u.q = handle;
- sft->start_cluster = u.w[0];
- sft->last_rel_cluster = u.w[1];
- sft->last_abs_cluster = u.w[2];
- sft->dir_sector = u.w[3];
-}
-
-#if ENABLE_DLOG
-static void print_handle(uint64_t handle)
-{
-
- union qword_to_words u;
- u.q = handle;
- dlog_printx(u.w[3]);
- dlog_putc('.');
- dlog_printx(u.w[2]);
- dlog_putc('.');
- dlog_printx(u.w[1]);
- dlog_putc('.');
- dlog_printx(u.w[0]);
-}
-#else
-#define print_handle(h) dlog_nop()
-#endif
-
static int get_op_drive_num(union INTPACK __far *r)
{
DOSSFT __far *sft;
@@ -213,6 +138,8 @@ static int vbox_err_to_dos(int32_t err)
return DOS_ERROR_PATH_NOT_FOUND;
case VERR_NO_MORE_FILES:
return DOS_ERROR_NO_MORE_FILES;
+ case VERR_ALREADY_EXISTS:
+ return DOS_ERROR_FILE_EXISTS;
default:
return DOS_ERROR_GEN_FAILURE;
}
@@ -341,6 +268,35 @@ static bool copy_to_8_3_filename(char __far *dst, const SHFLSTRING *str)
return valid_8_3;
}
+static uint16_t find_free_openfile()
+{
+ unsigned i;
+ for (i = 1; i < NUM_FILES; i++) {
+ if (data.files[i].root == SHFL_ROOT_NIL) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+static bool is_valid_openfile_index(unsigned index)
+{
+ if (index > NUM_FILES) return false;
+ if (index < 1) return false; // User programs cannot use index 0
+ if (data.files[index].root == SHFL_ROOT_NIL) return 0;
+ return true;
+}
+
+static inline void set_sft_openfile_index(DOSSFT __far *sft, unsigned index)
+{
+ sft->start_cluster = index;
+}
+
+static inline unsigned get_sft_openfile_index(DOSSFT __far *sft)
+{
+ return sft->start_cluster;
+}
+
static void handle_create_open_ex(union INTPACK __far *r)
{
const char __far *path = data.dossda->fn1;
@@ -348,6 +304,7 @@ static void handle_create_open_ex(union INTPACK __far *r)
SHFLROOT root = data.drives[drive].root;
DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
unsigned int action, mode;
+ unsigned openfile;
bool save_result;
int32_t err;
@@ -378,6 +335,12 @@ static void handle_create_open_ex(union INTPACK __far *r)
dlog_printx(mode);
dlog_endline();
+ openfile = find_free_openfile();
+ if (!openfile) {
+ set_dos_err(r, DOS_ERROR_ERROR_TOO_MANY_OPEN_FILES);
+ return;
+ }
+
copy_drive_relative_filename(&shflstr.shflstr, path);
translate_filename_to_host(&shflstr.shflstr);
@@ -395,9 +358,9 @@ static void handle_create_open_ex(union INTPACK __far *r)
createparms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
}
- if (mode == OPENEX_MODE_RDWR) {
+ if ((mode & OPENEX_MODE_RDWR) == OPENEX_MODE_RDWR) {
createparms.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
- } else if (mode == OPENEX_MODE_WRITE) {
+ } else if (mode & OPENEX_MODE_WRITE) {
createparms.CreateFlags |= SHFL_CF_ACCESS_WRITE;
} else {
createparms.CreateFlags |= SHFL_CF_ACCESS_READ;
@@ -415,18 +378,16 @@ static void handle_create_open_ex(union INTPACK __far *r)
dlog_print("vbox success result=");
dlog_printd(createparms.Result);
- dlog_print(" handle=");
- print_handle(createparms.Handle);
+ dlog_print(" openfile=");
+ dlog_printu(openfile);
dlog_endline();
switch (createparms.Result) {
case SHFL_PATH_NOT_FOUND:
- r->x.ax = DOS_ERROR_PATH_NOT_FOUND;
- r->x.flags |= INTR_CF;
+ set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND);
return;
case SHFL_FILE_NOT_FOUND:
- r->x.ax = DOS_ERROR_FILE_NOT_FOUND;
- r->x.flags |= INTR_CF;
+ set_dos_err(r, DOS_ERROR_FILE_NOT_FOUND);
return;
case SHFL_FILE_EXISTS:
if (save_result) r->x.cx = OPENEX_FILE_OPENED;
@@ -439,56 +400,74 @@ static void handle_create_open_ex(union INTPACK __far *r)
break;
}
+ if (createparms.Handle == SHFL_HANDLE_NIL) {
+ set_dos_err(r, DOS_ERROR_GEN_FAILURE);
+ return;
+ }
+
+ data.files[openfile].root = root;
+ data.files[openfile].handle = createparms.Handle;
+
// Fill in the SFT
map_shfl_info_to_dossft(sft, &createparms.Info);
sft->open_mode = mode;
sft->dev_info = 0x8040 | drive; // "Network drive, unwritten to"
sft->f_pos = 0;
- set_sft_vbox_handle(sft, createparms.Handle);
+ set_sft_openfile_index(sft, openfile);
clear_dos_err(r);
}
static void handle_close(union INTPACK __far *r)
{
- const char __far *path = data.dossda->fn1;
- int drive = drive_letter_to_index(path[0]);
- SHFLROOT root = data.drives[drive].root;
DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
- uint64_t handle = get_sft_vbox_handle(sft);
+ unsigned openfile = get_sft_openfile_index(sft);
int32_t err;
- dlog_print("handle_close for ");
- print_handle(handle);
+ dlog_print("handle_close openfile=");
+ dlog_printu(openfile);
dlog_endline();
- err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root, handle);
+ if (!is_valid_openfile_index(openfile)) {
+ set_dos_err(r, DOS_ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ err = vbox_shfl_close(&data.vb, data.hgcm_client_id,
+ data.files[openfile].root, data.files[openfile].handle);
if (err) {
set_vbox_err(r, err);
return;
}
+ data.files[openfile].root = SHFL_ROOT_NIL;
+ data.files[openfile].handle = SHFL_HANDLE_NIL;
+
clear_dos_err(r);
}
static void handle_read(union INTPACK __far *r)
{
DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
- int drive = sft->dev_info & 0x1F;
- SHFLROOT root = data.drives[drive].root;
- uint64_t handle = get_sft_vbox_handle(sft);
+ unsigned openfile = get_sft_openfile_index(sft);
uint8_t __far *buffer = data.dossda->cur_dta;
unsigned long offset = sft->f_pos;
unsigned bytes = r->x.cx;
int32_t err;
- dlog_print("handle_read handle=");
- print_handle(handle);
+ dlog_print("handle_read openfile=");
+ dlog_printu(openfile);
dlog_print(" bytes=");
dlog_printu(bytes);
dlog_endline();
- err = vbox_shfl_read(&data.vb, data.hgcm_client_id, root, handle,
+ if (!is_valid_openfile_index(openfile)) {
+ set_dos_err(r, DOS_ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ err = vbox_shfl_read(&data.vb, data.hgcm_client_id,
+ data.files[openfile].root, data.files[openfile].handle,
offset, &bytes, buffer);
if (err) {
set_vbox_err(r, err);
@@ -509,21 +488,25 @@ static void handle_read(union INTPACK __far *r)
static void handle_write(union INTPACK __far *r)
{
DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
- int drive = sft->dev_info & 0x1F;
- SHFLROOT root = data.drives[drive].root;
- uint64_t handle = get_sft_vbox_handle(sft);
+ unsigned openfile = get_sft_openfile_index(sft);
uint8_t __far *buffer = data.dossda->cur_dta;
unsigned long offset = sft->f_pos;
unsigned bytes = r->x.cx;
int32_t err;
- dlog_print("handle_write handle=");
- print_handle(handle);
+ dlog_print("handle_write openfile=");
+ dlog_printu(openfile);
dlog_print(" bytes=");
dlog_printu(bytes);
dlog_endline();
- err = vbox_shfl_write(&data.vb, data.hgcm_client_id, root, handle,
+ if (!is_valid_openfile_index(openfile)) {
+ set_dos_err(r, DOS_ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ err = vbox_shfl_write(&data.vb, data.hgcm_client_id,
+ data.files[openfile].root, data.files[openfile].handle,
offset, &bytes, buffer);
if (err) {
set_vbox_err(r, err);
@@ -544,6 +527,53 @@ static void handle_write(union INTPACK __far *r)
clear_dos_err(r);
}
+static void handle_close_all(union INTPACK __far *r)
+{
+ int32_t err;
+ unsigned i;
+
+ dlog_puts("handle_close_all");
+
+ for (i = 0; i < NUM_FILES; ++i) {
+ if (data.files[i].root != SHFL_ROOT_NIL) {
+ err = vbox_shfl_close(&data.vb, data.hgcm_client_id,
+ data.files[i].root, data.files[i].handle);
+ if (err) {
+ dlog_puts("vbox error on close all...");
+ // We'll leak this handle...
+ }
+
+ data.files[i].root = SHFL_ROOT_NIL;
+ data.files[i].handle = SHFL_HANDLE_NIL;
+ }
+ }
+
+ clear_dos_err(r);
+}
+
+static void handle_delete(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ int32_t err;
+
+ dlog_print("handle_delete ");
+ dlog_fprint(path);
+ dlog_endline();
+
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root,
+ &shflstr.shflstr, SHFL_REMOVE_FILE);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ clear_dos_err(r);
+}
static int32_t open_search_dir(SHFLROOT root, const char __far *path)
{
int32_t err;
@@ -574,26 +604,40 @@ static int32_t open_search_dir(SHFLROOT root, const char __far *path)
break;
}
- data.search_dir_handle = createparms.Handle;
+ if (createparms.Handle == SHFL_HANDLE_NIL) {
+ dlog_puts("open search dir returned no handle...");
+ return VERR_INVALID_HANDLE;
+ }
+
+ data.files[SEARCH_DIR_FILE].root = root;
+ data.files[SEARCH_DIR_FILE].handle = createparms.Handle;
+
return 0;
}
-static int close_search_dir(SHFLROOT root)
+static void close_search_dir()
{
int32_t err;
- if (data.search_dir_handle == SHFL_HANDLE_NIL) return 0;
-
- err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root,
- data.search_dir_handle);
-
- data.search_dir_handle = SHFL_HANDLE_NIL;
+ if (data.files[SEARCH_DIR_FILE].root == SHFL_ROOT_NIL) {
+ // Already closed
+ return;
+ }
+ err = vbox_shfl_close(&data.vb, data.hgcm_client_id,
+ data.files[SEARCH_DIR_FILE].root,
+ data.files[SEARCH_DIR_FILE].handle);
if (err) {
- return vbox_err_to_dos(err);
+ dlog_puts("vbox error on close_search_dir, ignoring");
}
- return 0;
+ data.files[SEARCH_DIR_FILE].root = SHFL_ROOT_NIL;
+ data.files[SEARCH_DIR_FILE].handle = SHFL_HANDLE_NIL;
+}
+
+static inline bool is_search_dir_open()
+{
+ return data.files[SEARCH_DIR_FILE].root != SHFL_ROOT_NIL;
}
static int32_t find_volume_label(SHFLROOT root)
@@ -618,18 +662,24 @@ static int32_t find_volume_label(SHFLROOT root)
return 0;
}
-static int32_t find_next_from_vbox(SHFLROOT root, uint8_t search_attr)
+static int32_t find_next_from_vbox(uint8_t search_attr)
{
DOSDIR __far *found_file = &data.dossda->found_file;
int32_t err;
+ if (!is_search_dir_open()) {
+ dlog_puts("find_next called, but no opendir handle");
+ return VERR_INVALID_HANDLE;
+ }
+
shfldirinfo.dirinfo.name.u16Size = sizeof(shfldirinfo.buf);
while (1) { // Loop until we have a valid file (or an error)
unsigned size = sizeof(shfldirinfo), resume = 0, count = 0;
dlog_puts("calling vbox list");
- err = vbox_shfl_list(&data.vb, data.hgcm_client_id, root, data.search_dir_handle,
+ err = vbox_shfl_list(&data.vb, data.hgcm_client_id,
+ data.files[SEARCH_DIR_FILE].root, data.files[SEARCH_DIR_FILE].handle,
SHFL_LIST_RETURN_ONE, &size, &shflstr.shflstr, &shfldirinfo.dirinfo,
&resume, &count);
if (err) {
@@ -684,7 +734,7 @@ static void handle_find(union INTPACK __far *r)
dlog_printx(search_attr);
dlog_endline();
- close_search_dir(root);
+ close_search_dir();
err = open_search_dir(root, path);
if (err) {
set_vbox_err(r, err);
@@ -726,12 +776,12 @@ static void handle_find(union INTPACK __far *r)
found_file->attr = 0;
- err = find_next_from_vbox(root, search_attr);
+ err = find_next_from_vbox(search_attr);
if (err) {
if (err == VERR_NO_MORE_FILES) {
dlog_puts("no more files");
}
- close_search_dir(root);
+ close_search_dir();
set_vbox_err(r, err);
return;
}
@@ -790,6 +840,70 @@ static void handle_chdir(union INTPACK __far *r)
clear_dos_err(r);
}
+static void handle_mkdir(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ int32_t err;
+
+ dlog_print("handle_mkdir ");
+ dlog_fprint(path);
+ dlog_endline();
+
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ memset(&createparms, 0, sizeof(SHFLCREATEPARMS));
+ createparms.CreateFlags = SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACT_CREATE_IF_NEW;
+
+ err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root,
+ &shflstr.shflstr, &createparms);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ switch (createparms.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ case SHFL_FILE_NOT_FOUND:
+ set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND);
+ return;
+ case SHFL_FILE_EXISTS:
+ set_dos_err(r, DOS_ERROR_FILE_EXISTS);
+ return;
+ default:
+ break;
+ }
+
+ clear_dos_err(r);
+}
+
+static void handle_rmdir(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ int32_t err;
+
+ dlog_print("handle_rmdir ");
+ dlog_fprint(path);
+ dlog_endline();
+
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root,
+ &shflstr.shflstr, SHFL_REMOVE_DIR);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ clear_dos_err(r);
+}
+
static bool int2f_11_handler(union INTPACK r)
#pragma aux int2f_11_handler "*" parm caller [] value [al] modify [ax bx cx dx si di es gs fs]
{
@@ -808,6 +922,14 @@ static bool int2f_11_handler(union INTPACK r)
dlog_printx(r.h.al);
dlog_endline();
+ // Handle special functions that target all redirectors first
+ switch (r.h.al) {
+ case DOS_FN_CLOSE_ALL:
+ handle_close_all(&r);
+ return false; // Let others do the same
+ }
+
+ // Now handle normal functions if they refer to our mounted drives
if (!is_call_for_mounted_drive(&r)) {
return false;
}
@@ -827,6 +949,9 @@ static bool int2f_11_handler(union INTPACK r)
case DOS_FN_WRITE:
handle_write(&r);
return true;
+ case DOS_FN_DELETE:
+ handle_delete(&r);
+ return true;
case DOS_FN_FIND_FIRST:
case DOS_FN_FIND_NEXT:
handle_find(&r);
@@ -834,6 +959,12 @@ static bool int2f_11_handler(union INTPACK r)
case DOS_FN_CHDIR:
handle_chdir(&r);
return true;
+ case DOS_FN_MKDIR:
+ handle_mkdir(&r);
+ return true;
+ case DOS_FN_RMDIR:
+ handle_rmdir(&r);
+ return true;
case DOS_FN_GET_DISK_FREE:
// We don't support this
set_dos_err(&r, DOS_ERROR_INVALID_FUNCTION);
diff --git a/sftsr.h b/sftsr.h
index eb0f5ee..0e5530c 100644
--- a/sftsr.h
+++ b/sftsr.h
@@ -26,30 +26,44 @@
#include "vbox.h"
#include "int21dos.h"
-#define VERSION_MAJOR 0
-#define VERSION_MINOR 1
-
#define LASTDRIVE 'Z'
#define MAX_NUM_DRIVE (LASTDRIVE - 'A')
#define NUM_DRIVES (MAX_NUM_DRIVE + 1)
-typedef struct tsrdata {
+/** Maximum number of open files */
+#define NUM_FILES 40
+
+/** Directory enumeration needs an open file, this is its index. */
+#define SEARCH_DIR_FILE 0
+
+typedef struct {
+ uint32_t root;
+ uint64_t handle;
+} OPENFILE;
+
+typedef struct {
// TSR installation data
/** Previous int2f ISR, storing it for uninstall. */
void (__interrupt __far *prev_int2f_handler)();
+ /** Stored pointer for the DOS SDA. */
+ DOSSDA __far *dossda;
+
+ // TSR configuration
+ /** Offset (in seconds/2) of the current timezone */
+ int32_t tz_offset;
+ // Current status
/** Array of all possible DOS drives. */
struct {
/** VirtualBox "root" for this drive, or NIL if unmounted. */
uint32_t root;
} drives[NUM_DRIVES];
- /** Stored pointer for the DOS SDA. */
- DOSSDA __far *dossda;
-
- /** Handle for the directory we are currently searching in. */
- uint64_t search_dir_handle;
+ /** All currently open files.
+ * index 0 is reserved for the file opened during directory enumeration. */
+ OPENFILE files[NUM_FILES];
+ // VirtualBox communication
struct vboxcomm vb;
uint32_t hgcm_client_id;
} TSRDATA;
diff --git a/unixtime.h b/unixtime.h
new file mode 100644
index 0000000..7dc4824
--- /dev/null
+++ b/unixtime.h
@@ -0,0 +1,130 @@
+#ifndef UNIXTIME_H
+#define UNIXTIME_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "dlog.h"
+
+#define UNIX_EPOCH_YEAR 1970
+#define DOS_EPOCH_YEAR 1980
+
+static bool is_leap_year(unsigned year)
+{
+ return ((year % 4) == 0 && (year % 100) != 0 || (year % 400) == 0);
+}
+
+static int days_per_year(unsigned year)
+{
+ return is_leap_year(year) ? 366 : 365;
+}
+
+static int days_per_month(unsigned month, bool leapyear)
+{
+ switch (month) {
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ return 31;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return 30;
+ case 2:
+ return leapyear ? 29 : 28;
+ }
+
+ return 0;
+}
+
+static void timestampns_to_dos_time(uint16_t __far *dos_time, uint16_t __far *dos_date, int64_t timestampns, int32_t tzoffset)
+{
+ // To maximize range of 32-bit ints, we'll use DOS's "2 seconds" as a unit instead of 1 second
+ long days_since_epoch = 0;
+ unsigned hours = 0, minutes = 0, seconds2 = 0;
+ unsigned year, month, day;
+ int per_year, per_month;
+ bool is_leap;
+
+ __asm {
+ push eax /* Preserve 32-bit regs */
+ push ecx
+ push edx
+ mov eax, dword ptr [timestampns]
+ mov edx, dword ptr [timestampns + 4]
+ mov ecx, 2 * 1000000000 /* nanoseconds in 2 seconds, should still fit in a dword */
+
+ idiv ecx /* 64-bit signed divison edx:eax / ecx, returns quotient in eax, remainder in edx */
+ /* TODO: Handle overflow, which will trigger an interrupt */
+
+ /* eax now contains seconds_since_epoch / 2 */
+ xor edx, edx /* Discard the remainder (less than 2 seconds) */
+
+ add eax, [tzoffset] /* Add tzoffset now (which is in seconds / 2 units) */
+
+ mov ecx, (24 * 60 * 60) / 2 /* seconds in one day / 2 */
+
+ idiv ecx
+ /* eax now contains days_since_epoch */
+ /* edx contains seconds (since start of day) /2 , this now fits in a 16-bit word */
+
+ mov [days_since_epoch], eax
+
+ mov eax, edx /* eax = seconds / 2 */
+ xor edx, edx
+ mov ecx, 60 / 2 /* seconds in a minute / 2; also clears upper part of ecx, we'll start using 16-bit registers from now on */
+
+ div cx
+ /* ax contains minutes, dx contains remainder seconds/2 -- should be < 30 */
+ mov [seconds2], dx
+
+ xor dx, dx
+ mov cx, 60 /* minutes per hour */
+ div cx
+ /* ax contains hours (< 24), dx contains remainder minutes (< 60) */
+ mov [minutes], dx
+ mov [hours], ax
+
+ pop edx
+ pop ecx
+ pop eax
+ }
+
+ year = UNIX_EPOCH_YEAR;
+ if (days_since_epoch > 0) {
+ while (days_since_epoch >= (per_year = days_per_year(year))) {
+ days_since_epoch -= per_year;
+ year++;
+ }
+ } else {
+ while (days_since_epoch < 0) {
+ days_since_epoch += days_per_year(year);
+ year--;
+ }
+ }
+
+ month = 1;
+ is_leap = is_leap_year(year);
+ while (days_since_epoch >= (per_month = days_per_month(month, is_leap))) {
+ days_since_epoch -= per_month;
+ month++;
+ }
+ day = 1 + days_since_epoch; // (day is 1-based)
+
+ if (year < DOS_EPOCH_YEAR) {
+ dlog_puts("Year is too old, will show as 0");
+ year = 0;
+ } else {
+ year -= DOS_EPOCH_YEAR;
+ }
+
+ *dos_time = ((hours << 11) & 0xF800) | ((minutes << 5) & 0x7E0) | (seconds2 & 0x1F);
+ *dos_date = ((year << 9) & 0xFE00) | ((month << 5) & 0x1E0) | (day & 0x1F);
+}
+
+#endif // UNIXTIME_H
diff --git a/vbox.h b/vbox.h
index d85263a..64e0c37 100644
--- a/vbox.h
+++ b/vbox.h
@@ -31,7 +31,8 @@
#include "utils.h"
#include "vboxdev.h"
-//#define VBOX_BUFFER_SIZE 64
+/** Size of the VBox buffer. The maximum message length that may be sent. */
+// Enough to fit a set_pointer_shape message with a 16x16 cursor
#define VBOX_BUFFER_SIZE (1024 + 32 + 24 + 20)
typedef struct vboxcomm {
diff --git a/vboxdev.h b/vboxdev.h
index 1b40f8e..93a0a7d 100644
--- a/vboxdev.h
+++ b/vboxdev.h
@@ -1111,20 +1111,20 @@ typedef struct SHFLFSOBJINFO
/** Time of last access (st_atime).
* @remarks Here (and other places) we depend on the IPRT timespec to
* remain unchanged. */
- uint64_t AccessTime;
+ int64_t AccessTime;
/** Time of last data modification (st_mtime). */
- uint64_t ModificationTime;
+ int64_t ModificationTime;
/** Time of last status change (st_ctime).
* If not available this is set to ModificationTime.
*/
- uint64_t ChangeTime;
+ int64_t ChangeTime;
/** Time of file birth (st_birthtime).
* If not available this is set to ChangeTime.
*/
- uint64_t BirthTime;
+ int64_t BirthTime;
/** Attributes. */
SHFLFSOBJATTR Attr;
@@ -1323,6 +1323,10 @@ typedef struct _SHFLDIRINFO
#define SHFL_LIST_RETURN_ONE 1
#define SHFL_LIST_RESTART 2
+#define SHFL_REMOVE_FILE (0x1)
+#define SHFL_REMOVE_DIR (0x2)
+#define SHFL_REMOVE_SYMLINK (0x4)
+
/** Query flag: Guest prefers drive letters as mount points. */
#define SHFL_MIQF_DRIVE_LETTER RT_BIT(0)
/** Query flag: Guest prefers paths as mount points. */
diff --git a/vboxshfl.h b/vboxshfl.h
index 292b8a3..aef9bca 100644
--- a/vboxshfl.h
+++ b/vboxshfl.h
@@ -362,6 +362,37 @@ static int32_t vbox_shfl_list(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROO
return req->header.result;
}
+static int32_t vbox_shfl_remove(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root,
+ const SHFLSTRING *path, unsigned flags)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_REMOVE, 3);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in shflstring "path"
+ req->aParms[1].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[1].u.Pointer.size = shflstring_size_with_buf(path);
+ req->aParms[1].u.Pointer.u.linearAddr = linear_addr(path);
+
+ // arg 2 in uint32 "flags"
+ req->aParms[2].type = VMMDevHGCMParmType_32bit;
+ req->aParms[2].u.value32 = flags;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+
static int32_t vbox_shfl_set_utf8(LPVBOXCOMM vb, hgcm_client_id_t client_id)
{
VMMDevHGCMCall __far *req = (void __far *) vb->buf;