aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-30 15:58:39 +0200
committerJavier <dev.git@javispedro.com>2022-04-30 15:58:39 +0200
commit4a852e27302524b6ae93cca256b690d6dea80435 (patch)
treec593121f8975d9c7a6d3de28c3989fb82e5af055
parent11d69ee443a869740102d53eb292598c8d159ff6 (diff)
downloadvbados-4a852e27302524b6ae93cca256b690d6dea80435.tar.gz
vbados-4a852e27302524b6ae93cca256b690d6dea80435.zip
support setting file date/time from dos
-rw-r--r--sftsr.c85
-rw-r--r--unixtime.h64
-rw-r--r--vboxshfl.h19
3 files changed, 167 insertions, 1 deletions
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)
{