diff options
-rw-r--r-- | dostsr.h | 24 | ||||
-rw-r--r-- | int21dos.h | 6 | ||||
-rw-r--r-- | mousmain.c | 4 | ||||
-rw-r--r-- | sfmain.c | 83 | ||||
-rw-r--r-- | sftsr.c | 373 | ||||
-rw-r--r-- | sftsr.h | 32 | ||||
-rw-r--r-- | unixtime.h | 130 | ||||
-rw-r--r-- | vbox.h | 3 | ||||
-rw-r--r-- | vboxdev.h | 12 | ||||
-rw-r--r-- | vboxshfl.h | 31 |
10 files changed, 542 insertions, 156 deletions
@@ -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; @@ -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 }; @@ -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 @@ -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; } @@ -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); @@ -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 @@ -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 { @@ -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. */ @@ -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; |