From 4a852e27302524b6ae93cca256b690d6dea80435 Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 30 Apr 2022 15:58:39 +0200 Subject: support setting file date/time from dos --- sftsr.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- unixtime.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++ vboxshfl.h | 19 ++++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) diff --git a/sftsr.c b/sftsr.c index 2294089..9e5023d 100644 --- a/sftsr.c +++ b/sftsr.c @@ -373,6 +373,7 @@ static inline void clear_sdb_openfile_index(DOSSDB __far *sdb) sdb->dir_entry = INVALID_OPENFILE; } +/** Closes an openfile entry by index, and marks it as free. */ static vboxerr close_openfile(unsigned index) { vboxerr err; @@ -392,6 +393,42 @@ static vboxerr close_openfile(unsigned index) return err; } +/** For an SFT corresponding to an openfile, + * flushes any pending metadata changes. + * Currently only time & date. */ +static void flush_sft_metadata(DOSSFT __far *sft) +{ + unsigned openfile = get_sft_openfile_index(sft); + vboxerr err; + + if (sft->dev_info & DOS_SFT_FLAG_TIME_SET) { + unsigned buf_size = sizeof(SHFLFSOBJINFO); + + dlog_puts("setting modified date/time"); + dlog_print("time="); + dlog_printx(sft->f_time); + dlog_print("date="); + dlog_printx(sft->f_date); + dlog_endline(); + + memset(&parms.objinfo, 0, sizeof(SHFLFSOBJINFO)); + + timestampns_from_dos_time(&parms.objinfo.ModificationTime, sft->f_time, sft->f_date, data.tz_offset); + + // Set the current file size + err = vbox_shfl_info(&data.vb, data.hgcm_client_id, + data.files[openfile].root, data.files[openfile].handle, + SHFL_INFO_SET | SHFL_INFO_FILE, &buf_size, &parms.objinfo); + if (err == 0) { + sft->dev_info &= ~DOS_SFT_FLAG_TIME_SET; + if (buf_size == sizeof(SHFLFSOBJINFO)) { + // VirtualBox returns updated information, let's use it + map_shfl_info_to_dossft(sft, &parms.objinfo); + } + } + } +} + static void handle_create_open_ex(union INTPACK __far *r) { const char __far *path = data.dossda->fn1; @@ -540,6 +577,8 @@ static void handle_close(union INTPACK __far *r) return; } + flush_sft_metadata(sft); + dos_sft_decref(sft); if (sft->num_handles == 0xFFFF) { // SFT is no longer referenced, really close file and clean it up @@ -634,10 +673,40 @@ static void handle_write(union INTPACK __far *r) // Assume the file has grown if we've written past the end if (sft->f_pos > sft->f_size) sft->f_size = sft->f_pos; + // Mark the SFT as dirty + sft->dev_info &= ~DOS_SFT_FLAG_CLEAN; + r->w.cx = bytes; clear_dos_err(r); } +static void handle_commit(union INTPACK __far *r) +{ + DOSSFT __far *sft = MK_FP(r->w.es, r->w.di); + unsigned openfile = get_sft_openfile_index(sft); + vboxerr err; + + dlog_print("handle_commit openfile="); + dlog_printu(openfile); + dlog_endline(); + + if (!is_valid_openfile_index(openfile)) { + set_dos_err(r, DOS_ERROR_INVALID_HANDLE); + return; + } + + flush_sft_metadata(sft); + + err = vbox_shfl_flush(&data.vb, data.hgcm_client_id, + data.files[openfile].root, data.files[openfile].handle); + if (err) { + set_vbox_err(r, err); + return; + } + + clear_dos_err(r); +} + static void handle_lock(union INTPACK __far *r) { DOSSFT __far *sft = MK_FP(r->w.es, r->w.di); @@ -681,7 +750,7 @@ static void handle_seek_end(union INTPACK __far *r) dlog_printd(offset); dlog_endline(); - memset(&parms.objinfo, buf_size, sizeof(SHFLFSOBJINFO)); + memset(&parms.objinfo, 0, sizeof(SHFLFSOBJINFO)); // Get the current file size err = vbox_shfl_info(&data.vb, data.hgcm_client_id, @@ -855,6 +924,14 @@ static void handle_getattr(union INTPACK __far *r) clear_dos_err(r); } +static void handle_setattr(union INTPACK __far *r) +{ + // TODO: Completely ignoring setattr, + // since the only attribute we could try to set + // in a multiplatform way is READONLY. + set_dos_err(r, DOS_ERROR_INVALID_FUNCTION); +} + /** Opens directory corresponding to file in path (i.e. we use the dirname), * filling corresponding openfile entry. */ static vboxerr open_search_dir(unsigned openfile, SHFLROOT root, const char __far *path) @@ -1356,6 +1433,9 @@ static bool int2f_11_handler(union INTPACK r) case DOS_FN_WRITE: handle_write(&r); return true; + case DOS_FN_COMMIT: + handle_commit(&r); + return true; case DOS_FN_LOCK: handle_lock(&r); return true; @@ -1371,6 +1451,9 @@ static bool int2f_11_handler(union INTPACK r) case DOS_FN_GET_FILE_ATTR: handle_getattr(&r); return true; + case DOS_FN_SET_FILE_ATTR: + handle_setattr(&r); + return true; case DOS_FN_FIND_FIRST: handle_find_first(&r); return true; diff --git a/unixtime.h b/unixtime.h index 296c8ac..57846b7 100644 --- a/unixtime.h +++ b/unixtime.h @@ -150,4 +150,68 @@ static void timestampns_to_dos_time(uint16_t __far *dos_time, uint16_t __far *do *dos_date = ((year << 9) & 0xFE00) | ((month << 5) & 0x1E0) | (day & 0x1F); } +static void timestampns_from_dos_time(int64_t *timestampns, uint16_t dos_time, uint16_t dos_date, int32_t tzoffset) +{ + unsigned year = (dos_date & 0xFE00) >> 9, + month = (dos_date & 0x1E0) >> 5, + day = (dos_date & 0x1F), + hours = (dos_time & 0xF800) >> 11, + minutes = (dos_time & 0x7E0) >> 5, + seconds2 = (dos_time & 0x1F); + long days_since_epoch = 0; + long seconds2_since_day; + bool is_leap; + + year += DOS_EPOCH_YEAR; + is_leap = is_leap_year(year); + + while (year > UNIX_EPOCH_YEAR) { + days_since_epoch += days_per_year(--year); + } + while (year < UNIX_EPOCH_YEAR) { + days_since_epoch -= days_per_year(year++); + } + + while (month > 1) { + days_since_epoch += days_per_month(--month, is_leap); + } + days_since_epoch += day - 1; + + seconds2_since_day = seconds2 + (minutes * 60U/2) + (hours * 3600U/2); + + dlog_print("days_since_epoch="); + dlog_printd(days_since_epoch); + dlog_print(" seconds2_since_day="); + dlog_printd(seconds2_since_day); + dlog_endline(); + + __asm { + push eax + push ecx + push edx + + mov eax, [days_since_epoch] + mov ecx, (24 * 60 * 60) / 2 /* seconds in one day / 2 */ + + imul eax, ecx + + add eax, [seconds2_since_day] + /* eax now contains seconds_since_epoch / 2 */ + + add eax, [tzoffset] /* Add tzoffset now (which is in seconds / 2 units) */ + + mov ecx, 2 * 1000000000 /* nanoseconds in 2 seconds, should still fit in a dword */ + + imul ecx /* 64-bit signed multiply eax * ecx, returns result in edx:eax */ + + mov si, [timestampns] + mov dword ptr [si], eax + mov dword ptr [si + 4], edx + + pop edx + pop ecx + pop eax + } +} + #endif // UNIXTIME_H diff --git a/vboxshfl.h b/vboxshfl.h index 097d522..d9da724 100644 --- a/vboxshfl.h +++ b/vboxshfl.h @@ -294,6 +294,25 @@ static vboxerr vbox_shfl_lock(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROO return req->header.result; } +static vboxerr vbox_shfl_flush(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle) +{ + VMMDevHGCMCall __far *req = (void __far *) vb->buf; + vboxerr err; + + vbox_hgcm_init_call(req, client_id, SHFL_FN_FLUSH, 2); + + // arg 0 in uint32 "root" + vbox_hgcm_set_parameter_shflroot(req, 0, root); + + // arg 1 in uint64 "handle" + vbox_hgcm_set_parameter_shflhandle(req, 1, handle); + + if ((err = vbox_hgcm_do_call_sync(vb, req)) < 0) + return err; + + return req->header.result; +} + static vboxerr vbox_shfl_list(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle, unsigned flags, unsigned __far *size, const SHFLSTRING *path, SHFLDIRINFO *dirinfo, unsigned __far *resume, unsigned __far *count) { -- cgit v1.2.3