diff options
| author | Javier <dev.git@javispedro.com> | 2022-04-12 22:38:20 +0200 | 
|---|---|---|
| committer | Javier <dev.git@javispedro.com> | 2022-04-12 22:38:20 +0200 | 
| commit | a109a1193ca3cf64a29265103075922d2dd9fc1b (patch) | |
| tree | cde59f7e29ccca176dd150a51e8caf4613a86d1f | |
| parent | 2b9f509a04acb88d9ac72bce23b28099efdc4878 (diff) | |
| download | vbados-a109a1193ca3cf64a29265103075922d2dd9fc1b.tar.gz vbados-a109a1193ca3cf64a29265103075922d2dd9fc1b.zip  | |
add file date/time listing support, closeall call
| -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;  | 
