1 /**
2   * UAE - The Un*x Amiga Emulator
3   *
4   * Hardfile emulation for *nix systems
5   *
6   * Copyright 2003-2006 Richard Drummond
7   * Copyright 2008-2010 Mustafa TUFAN
8   * Based on hardfile_win32.c
9   */
10 
11 #include "sysconfig.h"
12 #include "sysdeps.h"
13 
14 #include "options.h"
15 #include "filesys.h"
16 #include "zfile.h"
17 #include "uae/fs.h"
18 #include "uae/io.h"
19 #include "uae/log.h"
20 
21 #ifdef MACOSX
22 #include <sys/stat.h>
23 #include <sys/disk.h>
24 #endif
25 
26 #ifdef OPENBSD
27 #include <sys/types.h>
28 #include <sys/disklabel.h>
29 #include <sys/dkio.h>
30 #include <sys/ioctl.h>
31 #include <fcntl.h>
32 #endif
33 
34 #define hfd_log write_log
35 static int g_debug = 0;
36 
37 //#define HDF_DEBUG
38 #ifdef  HDF_DEBUG
39 #define DEBUG_LOG write_log ( "%s: ", __func__); write_log
40 #else
41 #define DEBUG_LOG(...) do ; while(0)
42 #endif
43 
44 struct hardfilehandle
45 {
46     int zfile;
47     struct zfile *zf;
48     FILE *h;
49 };
50 
51 struct uae_driveinfo {
52     char vendor_id[128];
53     char product_id[128];
54     char product_rev[128];
55     char product_serial[128];
56     char device_name[2048];
57     char device_path[2048];
58     uae_u64 size;
59     uae_u64 offset;
60     int bytespersector;
61     int removablemedia;
62     int nomedia;
63     int dangerous;
64     int readonly;
65 };
66 
67 #define HDF_HANDLE_WIN32 1
68 #define HDF_HANDLE_ZFILE 2
69 #define HDF_HANDLE_LINUX 3
70 #undef INVALID_HANDLE_VALUE
71 #define INVALID_HANDLE_VALUE NULL
72 
73 #define CACHE_SIZE 16384
74 #define CACHE_FLUSH_TIME 5
75 
76 /* safety check: only accept drives that:
77 * - contain RDSK in block 0
78 * - block 0 is zeroed
79 */
80 
81 int harddrive_dangerous, do_rdbdump;
82 static struct uae_driveinfo uae_drives[MAX_FILESYSTEM_UNITS];
83 
rdbdump(FILE * h,uae_u64 offset,uae_u8 * buf,int blocksize)84 static void rdbdump (FILE *h, uae_u64 offset, uae_u8 *buf, int blocksize)
85 {
86     static int cnt = 1;
87     int i, blocks;
88     char name[100];
89     FILE *f;
90 
91     blocks = (buf[132] << 24) | (buf[133] << 16) | (buf[134] << 8) | (buf[135] << 0);
92     if (blocks < 0 || blocks > 100000)
93         return;
94     _stprintf (name, "rdb_dump_%d.rdb", cnt);
95     f = uae_tfopen (name, "wb");
96     if (!f)
97         return;
98     for (i = 0; i <= blocks; i++) {
99         if (uae_fseeko64 (h, offset, SEEK_SET) != 0)
100             break;
101         int outlen = fread (buf, 1, blocksize, h);
102         if (outlen != blocksize) {
103             write_log("rdbdump: warning: read %d bytes (not blocksize %d)\n",
104                       outlen, blocksize);
105         }
106         fwrite (buf, 1, blocksize, f);
107         offset += blocksize;
108     }
109     fclose (f);
110     cnt++;
111 }
112 
113 //static int ismounted (int hd)
114 //FIXME:
ismounted(FILE * f)115 static int ismounted (FILE* f) {
116     STUB("");
117     int mounted;
118     //mounted = 1;
119     mounted = 0;
120     return mounted;
121 }
122 
123 #define CA "Commodore\0Amiga\0"
safetycheck(FILE * h,const char * name,uae_u64 offset,uae_u8 * buf,int blocksize)124 static int safetycheck (FILE *h, const char *name, uae_u64 offset, uae_u8 *buf, int blocksize)
125 {
126     int i, j, blocks = 63, empty = 1;
127     long outlen;
128 
129     for (j = 0; j < blocks; j++) {
130         if (uae_fseeko64 (h, offset, SEEK_SET) != 0) {
131             write_log ("hd ignored, SetFilePointer failed, error %d\n", errno);
132             return 1;
133         }
134         memset (buf, 0xaa, blocksize);
135         outlen = fread (buf, 1, blocksize, h);
136         if (outlen != blocksize) {
137             write_log ("hd ignored, read error %d!\n", errno);
138             return 2;
139         }
140         if (j == 0 && offset > 0)
141             return -5;
142         if (j == 0 && buf[0] == 0x39 && buf[1] == 0x10 && buf[2] == 0xd3 && buf[3] == 0x12) {
143             // ADIDE "CPRM" hidden block..
144             if (do_rdbdump)
145                 rdbdump (h, offset, buf, blocksize);
146             write_log ("hd accepted (adide rdb detected at block %d)\n", j);
147             return -3;
148         }
149         if (!memcmp (buf, "RDSK", 4) || !memcmp (buf, "DRKS", 4)) {
150             if (do_rdbdump)
151                 rdbdump (h, offset, buf, blocksize);
152             write_log ("hd accepted (rdb detected at block %d)\n", j);
153             return -1;
154         }
155 
156         if (!memcmp (buf + 2, "CIS@", 4) && !memcmp (buf + 16, CA, strlen (CA))) {
157             write_log ("hd accepted (PCMCIA RAM)\n");
158             return -2;
159         }
160         if (j == 0) {
161             for (i = 0; i < blocksize; i++) {
162                 if (buf[i])
163                     empty = 0;
164             }
165         }
166         offset += blocksize;
167     }
168     if (!empty) {
169         int mounted;
170         mounted = ismounted (h);
171         if (!mounted) {
172             write_log ("hd accepted, not empty and not mounted in Windows\n");
173             return -8;
174         }
175         if (mounted < 0) {
176             write_log ("hd ignored, NTFS partitions\n");
177             return 0;
178         }
179         if (harddrive_dangerous == 0x1234dead)
180             return -6;
181         write_log ("hd ignored, not empty and no RDB detected or Windows mounted\n");
182         return 0;
183     }
184     write_log ("hd accepted (empty)\n");
185     return -9;
186 }
187 
188 /*
189 static void trim (TCHAR *s)
190 {
191     while(_tcslen(s) > 0 && s[_tcslen(s) - 1] == ' ') {
192         s[_tcslen(s) - 1] = 0;
193     }
194 }
195 */
196 
isharddrive(const TCHAR * name)197 static int isharddrive (const TCHAR *name)
198 {
199     int i;
200 
201     for (i = 0; i < hdf_getnumharddrives (); i++) {
202         if (!_tcscmp (uae_drives[i].device_name, name))
203             return i;
204     }
205     return -1;
206 }
207 
208 static const char *hdz[] = { "hdz", "zip", "rar", "7z", NULL };
209 
hdf_open_target(struct hardfiledata * hfd,const char * pname)210 int hdf_open_target (struct hardfiledata *hfd, const char *pname)
211 {
212     FILE *h = INVALID_HANDLE_VALUE;
213     int i;
214     struct uae_driveinfo *udi;
215     char *name = strdup (pname);
216 
217     if (getenv("FS_DEBUG_HDF")) {
218         g_debug = 1;
219     }
220     if (g_debug) {
221         write_log("\n\n-- hdf_open_target pname = %s\n", pname);
222     }
223 
224     hfd->flags = 0;
225     hfd->drive_empty = 0;
226     hdf_close (hfd);
227     hfd->cache = (uae_u8*)xmalloc (uae_u8, CACHE_SIZE);
228     hfd->cache_valid = 0;
229     hfd->virtual_size = 0;
230     hfd->virtual_rdb = NULL;
231     if (!hfd->cache) {
232         write_log ("VirtualAlloc(%d) failed, error %d\n", CACHE_SIZE, errno);
233         goto end;
234     }
235     hfd->handle = xcalloc (struct hardfilehandle, 1);
236     hfd->handle->h = INVALID_HANDLE_VALUE;
237     hfd_log ("hfd open: '%s'\n", name);
238     if (_tcslen (name) > 4 && !_tcsncmp (name,"HD_", 3)) {
239         hdf_init_target ();
240         i = isharddrive (name);
241         if (i >= 0) {
242             udi = &uae_drives[i];
243             hfd->flags = HFD_FLAGS_REALDRIVE;
244             if (udi->nomedia)
245                 hfd->drive_empty = -1;
246             if (udi->readonly)
247                 hfd->ci.readonly = 1;
248             h = uae_tfopen (udi->device_path, hfd->ci.readonly ? "rb" : "r+b");
249             hfd->handle->h = h;
250             if (h == INVALID_HANDLE_VALUE)
251                 goto end;
252             _tcsncpy (hfd->vendor_id, udi->vendor_id, 8);
253             _tcsncpy (hfd->product_id, udi->product_id, 16);
254             _tcsncpy (hfd->product_rev, udi->product_rev, 4);
255             hfd->offset = udi->offset;
256             hfd->physsize = hfd->virtsize = udi->size;
257             hfd->ci.blocksize = udi->bytespersector;
258             if (hfd->offset == 0 && !hfd->drive_empty) {
259                 int sf = safetycheck (hfd->handle->h, udi->device_path, 0, hfd->cache, hfd->ci.blocksize);
260                 if (sf > 0)
261                     goto end;
262                 if (sf == 0 && !hfd->ci.readonly && harddrive_dangerous != 0x1234dead) {
263                     write_log ("'%s' forced read-only, safetycheck enabled\n", udi->device_path);
264                     hfd->dangerous = 1;
265                     // clear GENERIC_WRITE
266                     fclose (h);
267                     h = uae_tfopen (udi->device_path, "r+b");
268                     hfd->handle->h = h;
269                     if (h == INVALID_HANDLE_VALUE)
270                         goto end;
271                 }
272             }
273             hfd->handle_valid = HDF_HANDLE_LINUX;
274             hfd->emptyname = strdup (name);
275         } else {
276             hfd->flags = HFD_FLAGS_REALDRIVE;
277             hfd->drive_empty = -1;
278             hfd->emptyname = strdup (name);
279         }
280     } else {
281         int zmode = 0;
282         char *ext = _tcsrchr (name, '.');
283         if (ext != NULL) {
284             ext++;
285             for (i = 0; hdz[i]; i++) {
286                 if (!_tcsicmp (ext, hdz[i]))
287                     zmode = 1;
288             }
289         }
290         h = uae_tfopen (name, hfd->ci.readonly ? "rb" : "r+b");
291         if (h == INVALID_HANDLE_VALUE)
292             goto end;
293         hfd->handle->h = h;
294         i = _tcslen (name) - 1;
295         while (i >= 0) {
296             if ((i > 0 && (name[i - 1] == '/' || name[i - 1] == '\\')) || i == 0) {
297                 _tcscpy (hfd->vendor_id, "UAE");
298                 _tcsncpy (hfd->product_id, name + i, 15);
299                 _tcscpy (hfd->product_rev, "0.3");
300                 break;
301             }
302             i--;
303         }
304         if (h != INVALID_HANDLE_VALUE) {
305             // determine size of hdf file
306             int ret;
307             off_t low = -1;
308 
309 #if defined(MACOSX) || defined(OPENBSD)
310             // check type of file
311             struct stat st;
312             ret = stat(name,&st);
313             if (ret) {
314                 write_log("osx: can't stat '%s'\n", name);
315                 goto end;
316             }
317             // block devices need special handling on BSD and OSX
318             if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
319                 int fh = fileno(h);
320 #if defined(MACOSX)
321                 uint32_t block_size;
322                 uint64_t block_count;
323                 // get number of blocks
324                 ret = ioctl(fh, DKIOCGETBLOCKCOUNT, &block_count);
325                 if (ret) {
326                     write_log("osx: can't get block count of '%s' (%d)\n",
327                             name, fh);
328                     goto end;
329                 }
330                 // get block size
331                 ret = ioctl(fh, DKIOCGETBLOCKSIZE, &block_size);
332                 if (ret) {
333                     write_log("osx: can't get block size of '%s' (%d)\n",
334                             name, fh);
335                     goto end;
336                 }
337                 write_log("osx: found raw device: block_size=%u "
338                         "block_count=%llu\n", block_size, block_count);
339                 low = block_size * block_count;
340 #elif defined(OPENBSD)
341                 struct disklabel label;
342                 if (ioctl(fh, DIOCGDINFO, &label) < 0) {
343                     write_log("openbsd: can't get disklabel of '%s' (%d)\n", name, fh);
344                     goto end;
345                 }
346                 write_log("openbsd: bytes per sector: %u\n", label.d_secsize);
347                 write_log("openbsd: sectors per unit: %u\n", label.d_secperunit);
348                 low = label.d_secsize * label.d_secperunit;
349                 write_log("openbsd: total bytes: %llu\n", low);
350 #endif
351             }
352 #endif // OPENBSD || MACOSX
353 
354             if (low == -1) {
355                 // assuming regular file; seek to end and ftell
356                 ret = uae_fseeko64 (h, 0, SEEK_END);
357                 if (ret)
358                     goto end;
359                 low = uae_ftello64 (h);
360                 if (low == -1)
361                     goto end;
362             }
363 
364             low &= ~(hfd->ci.blocksize - 1);
365             hfd->physsize = hfd->virtsize = low;
366             if (g_debug) {
367                 write_log("set physsize = virtsize = %lld (low)\n",
368                         hfd->virtsize);
369             }
370             hfd->handle_valid = HDF_HANDLE_LINUX;
371             if (hfd->physsize < 64 * 1024 * 1024 && zmode) {
372                 write_log ("HDF '%s' re-opened in zfile-mode\n", name);
373                 fclose (h);
374                 hfd->handle->h = INVALID_HANDLE_VALUE;
375                 hfd->handle->zf = zfile_fopen(name, hfd->ci.readonly ? "rb" : "r+b", ZFD_NORMAL);
376                 hfd->handle->zfile = 1;
377                 if (!h)
378                     goto end;
379                 zfile_fseek (hfd->handle->zf, 0, SEEK_END);
380                 hfd->physsize = hfd->virtsize = zfile_ftell (hfd->handle->zf);
381                 if (g_debug) {
382                     write_log("set physsize = virtsize = %lld\n",
383                             hfd->virtsize);
384                 }
385                 zfile_fseek (hfd->handle->zf, 0, SEEK_SET);
386                 hfd->handle_valid = HDF_HANDLE_ZFILE;
387             }
388         } else {
389             write_log ("HDF '%s' failed to open. error = %d\n", name, errno);
390         }
391     }
392     if (hfd->handle_valid || hfd->drive_empty) {
393         hfd_log ("HDF '%s' opened, size=%dK mode=%d empty=%d\n",
394             name, (int) (hfd->physsize / 1024), hfd->handle_valid, hfd->drive_empty);
395         return 1;
396     }
397 end:
398     hdf_close (hfd);
399     xfree (name);
400     return 0;
401 }
402 
403 /*
404 static void freehandle (struct hardfilehandle *h)
405 {
406     if (!h)
407         return;
408     if (!h->zfile && h->h != INVALID_HANDLE_VALUE)
409         fclose (h->h);
410     if (h->zfile && h->zf)
411         zfile_fclose (h->zf);
412     h->zf = NULL;
413     h->h = INVALID_HANDLE_VALUE;
414     h->zfile = 0;
415 }
416 */
417 
hdf_close_target(struct hardfiledata * hfd)418 void hdf_close_target (struct hardfiledata *hfd) {
419     write_log("hdf_close_target\n");
420     if (hfd->handle && hfd->handle->h) {
421         write_log("closing file handle %p\n", hfd->handle->h);
422         fclose(hfd->handle->h);
423     }
424     //freehandle (hfd->handle);
425     xfree (hfd->handle);
426     xfree (hfd->emptyname);
427     hfd->emptyname = NULL;
428     hfd->handle = NULL;
429     hfd->handle_valid = 0;
430     if (hfd->cache)
431         xfree (hfd->cache);
432     xfree(hfd->virtual_rdb);
433     hfd->virtual_rdb = 0;
434     hfd->virtual_size = 0;
435     hfd->cache = 0;
436     hfd->cache_valid = 0;
437     hfd->drive_empty = 0;
438     hfd->dangerous = 0;
439 }
440 
hdf_dup_target(struct hardfiledata * dhfd,const struct hardfiledata * shfd)441 int hdf_dup_target (struct hardfiledata *dhfd, const struct hardfiledata *shfd)
442 {
443     if (!shfd->handle_valid)
444         return 0;
445 
446     return 0;
447 }
448 
hdf_seek(struct hardfiledata * hfd,uae_u64 offset)449 static int hdf_seek (struct hardfiledata *hfd, uae_u64 offset)
450 {
451     size_t ret;
452 
453     if (hfd->handle_valid == 0) {
454         gui_message ("hd: hdf handle is not valid. bug.");
455         abort();
456     }
457     if (offset >= hfd->physsize - hfd->virtual_size) {
458         gui_message ("hd: tried to seek out of bounds! (0x%llx >= 0x%llx)\n", offset, hfd->physsize);
459         abort ();
460     }
461     offset += hfd->offset;
462     if (offset & (hfd->ci.blocksize - 1)) {
463         gui_message ("hd: poscheck failed, offset=0x%llx not aligned to blocksize=%d! (0x%llx & 0x%04.4x = 0x%04.4x)\n",
464             offset, hfd->ci.blocksize, offset, hfd->ci.blocksize, offset & (hfd->ci.blocksize - 1));
465         abort ();
466     }
467     if (hfd->handle_valid == HDF_HANDLE_LINUX) {
468         ret = uae_fseeko64 (hfd->handle->h, offset, SEEK_SET);
469         if (ret) {
470             write_log("hdf_seek failed\n");
471             return -1;
472         }
473     } else if (hfd->handle_valid == HDF_HANDLE_ZFILE) {
474         zfile_fseek (hfd->handle->zf, (long)offset, SEEK_SET);
475     }
476     return 0;
477 }
478 
poscheck(struct hardfiledata * hfd,int len)479 static void poscheck (struct hardfiledata *hfd, int len)
480 {
481     int ret;
482     uae_u64 pos = 0;
483 
484     if (hfd->handle_valid == HDF_HANDLE_LINUX) {
485         ret = uae_fseeko64 (hfd->handle->h, 0, SEEK_CUR);
486         if (ret) {
487             gui_message ("hd: poscheck failed. seek failure, ret %d", ret);
488             abort ();
489         }
490         pos = uae_ftello64 (hfd->handle->h);
491     } else if (hfd->handle_valid == HDF_HANDLE_ZFILE) {
492         pos = zfile_ftell (hfd->handle->zf);
493     }
494     if (len < 0) {
495         gui_message ("hd: poscheck failed, negative length! (%d)", len);
496         abort ();
497     }
498     if (pos < hfd->offset) {
499         gui_message ("hd: poscheck failed, offset out of bounds! (0x%llx < 0x%llx)", pos, hfd->offset);
500         abort ();
501     }
502     if (pos >= hfd->offset + hfd->physsize - hfd->virtual_size || pos >= hfd->offset + hfd->physsize + len - hfd->virtual_size) {
503         gui_message ("hd: poscheck failed, offset out of bounds! (0x%llx >= 0x%llx, LEN=%d)", pos, hfd->offset + hfd->physsize, len);
504         abort ();
505     }
506     if (pos & (hfd->ci.blocksize - 1)) {
507         gui_message ("hd: poscheck failed, offset not aligned to blocksize! (0x%llx & 0x%04.4x = 0x%04.4x\n", pos, hfd->ci.blocksize, pos & hfd->ci.blocksize);
508         abort ();
509     }
510 }
511 
isincache(struct hardfiledata * hfd,uae_u64 offset,int len)512 static int isincache (struct hardfiledata *hfd, uae_u64 offset, int len)
513 {
514     if (!hfd->cache_valid)
515         return -1;
516     if (offset >= hfd->cache_offset && offset + len <= hfd->cache_offset + CACHE_SIZE)
517         return (int)(offset - hfd->cache_offset);
518     return -1;
519 }
520 
521 #if 0
522 void hfd_flush_cache (struct hardfiledata *hfd, int now)
523 {
524     DWORD outlen = 0;
525     if (!hfd->cache_needs_flush || !hfd->cache_valid)
526         return;
527     if (now || time (NULL) > hfd->cache_needs_flush + CACHE_FLUSH_TIME) {
528         hdf_log ("flushed %d %d %d\n", now, time(NULL), hfd->cache_needs_flush);
529         hdf_seek (hfd, hfd->cache_offset);
530         poscheck (hfd, CACHE_SIZE);
531         WriteFile (hfd->handle, hfd->cache, CACHE_SIZE, &outlen, NULL);
532         hfd->cache_needs_flush = 0;
533     }
534 }
535 #endif
536 
hdf_read_2(struct hardfiledata * hfd,void * buffer,uae_u64 offset,int len)537 static int hdf_read_2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len)
538 {
539     long outlen = 0;
540     int coffset;
541 
542     if (offset == 0)
543         hfd->cache_valid = 0;
544     coffset = isincache (hfd, offset, len);
545     if (coffset >= 0) {
546         memcpy (buffer, hfd->cache + coffset, len);
547         return len;
548     }
549     hfd->cache_offset = offset;
550     if (offset + CACHE_SIZE > hfd->offset + (hfd->physsize - hfd->virtual_size))
551         hfd->cache_offset = hfd->offset + (hfd->physsize - hfd->virtual_size) - CACHE_SIZE;
552     hdf_seek (hfd, hfd->cache_offset);
553     poscheck (hfd, CACHE_SIZE);
554     if (hfd->handle_valid == HDF_HANDLE_LINUX)
555         outlen = fread (hfd->cache, 1, CACHE_SIZE, hfd->handle->h);
556     else if (hfd->handle_valid == HDF_HANDLE_ZFILE)
557         outlen = zfile_fread (hfd->cache, 1, CACHE_SIZE, hfd->handle->zf);
558     hfd->cache_valid = 0;
559     if (outlen != CACHE_SIZE)
560         return 0;
561     hfd->cache_valid = 1;
562     coffset = isincache (hfd, offset, len);
563     if (coffset >= 0) {
564         memcpy (buffer, hfd->cache + coffset, len);
565         return len;
566     }
567     write_log ("hdf_read: cache bug! offset=0x%llx len=%d\n", offset, len);
568     hfd->cache_valid = 0;
569     return 0;
570 }
571 
hdf_read_target(struct hardfiledata * hfd,void * buffer,uae_u64 offset,int len)572 int hdf_read_target (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len)
573 {
574     int got = 0;
575     uae_u8 *p = (uae_u8*)buffer;
576 
577     if (hfd->drive_empty)
578         return 0;
579     if (offset < hfd->virtual_size) {
580         uae_u64 len2 = offset + len <= hfd->virtual_size ? len : hfd->virtual_size - offset;
581         if (!hfd->virtual_rdb)
582             return 0;
583         memcpy (buffer, hfd->virtual_rdb + offset, len2);
584         return len2;
585     }
586     offset -= hfd->virtual_size;
587     while (len > 0) {
588         int maxlen;
589         int ret = 0;
590         if (hfd->physsize < CACHE_SIZE) {
591             hfd->cache_valid = 0;
592             hdf_seek (hfd, offset);
593             poscheck (hfd, len);
594             if (hfd->handle_valid == HDF_HANDLE_LINUX) {
595                 ret = fread (hfd->cache, 1, len, hfd->handle->h);
596                 memcpy (buffer, hfd->cache, ret);
597             } else if (hfd->handle_valid == HDF_HANDLE_ZFILE) {
598                 ret = zfile_fread (buffer, 1, len, hfd->handle->zf);
599             }
600             maxlen = len;
601         } else {
602             maxlen = len > CACHE_SIZE ? CACHE_SIZE : len;
603             ret = hdf_read_2 (hfd, p, offset, maxlen);
604         }
605         got += ret;
606         if (ret != maxlen)
607             return got;
608         offset += maxlen;
609         p += maxlen;
610         len -= maxlen;
611     }
612     return got;
613 }
614 
hdf_write_2(struct hardfiledata * hfd,void * buffer,uae_u64 offset,int len)615 static int hdf_write_2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len)
616 {
617     int outlen = 0;
618 
619     if (hfd->ci.readonly) {
620         if (g_debug) {
621             write_log("hfd->readonly\n");
622         }
623         return 0;
624     }
625     if (hfd->dangerous) {
626         if (g_debug) {
627             write_log("hfd->dangerous\n");
628         }
629         return 0;
630     }
631     hfd->cache_valid = 0;
632     hdf_seek (hfd, offset);
633     poscheck (hfd, len);
634     memcpy (hfd->cache, buffer, len);
635     if (hfd->handle_valid == HDF_HANDLE_LINUX) {
636         outlen = fwrite (hfd->cache, 1, len, hfd->handle->h);
637         //fflush(hfd->handle->h);
638         if (g_debug) {
639             write_log("wrote %u bytes (wanted %d) at offset %llx\n", outlen,
640                     len, offset);
641         }
642         const TCHAR *name = hfd->emptyname == NULL ? _T("<unknown>") : hfd->emptyname;
643         if (offset == 0) {
644             long outlen2;
645             uae_u8 *tmp;
646             int tmplen = 512;
647             tmp = (uae_u8*)xmalloc (uae_u8, tmplen);
648             if (tmp) {
649                 memset (tmp, 0xa1, tmplen);
650                 hdf_seek (hfd, offset);
651                 outlen2 = fread (tmp, 1, tmplen, hfd->handle->h);
652                 if (memcmp (hfd->cache, tmp, tmplen) != 0 || outlen != len)
653                     gui_message (_T("\"%s\"\n\nblock zero write failed!"), name);
654                 xfree (tmp);
655             }
656         }
657     } else if (hfd->handle_valid == HDF_HANDLE_ZFILE) {
658         outlen = zfile_fwrite (hfd->cache, 1, len, hfd->handle->zf);
659     }
660     return outlen;
661 }
hdf_write_target(struct hardfiledata * hfd,void * buffer,uae_u64 offset,int len)662 int hdf_write_target (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len)
663 {
664     int got = 0;
665     uae_u8 *p = (uae_u8*)buffer;
666 
667     if (g_debug) {
668         write_log("hdf_write_target off %llx len %d virtual size %lld\n",
669                 offset, len, hfd->virtual_size);
670     }
671     if (hfd->drive_empty) {
672         if (g_debug) {
673             write_log("hfd->drive_empty\n");
674         }
675         return 0;
676     }
677     if (offset < hfd->virtual_size) {
678         if (g_debug) {
679             write_log("offset < hfd->virtual_size\n");
680         }
681         return len;
682     }
683     offset -= hfd->virtual_size;
684     while (len > 0) {
685         int maxlen = len > CACHE_SIZE ? CACHE_SIZE : len;
686         int ret = hdf_write_2 (hfd, p, offset, maxlen);
687         if (ret < 0)
688             return ret;
689         got += ret;
690         if (ret != maxlen)
691             return got;
692         offset += maxlen;
693         p += maxlen;
694         len -= maxlen;
695     }
696     return got;
697 }
698 
hdf_resize_target(struct hardfiledata * hfd,uae_u64 newsize)699 int hdf_resize_target(struct hardfiledata *hfd, uae_u64 newsize)
700 {
701     if (newsize < hfd->physsize) {
702         uae_log("hdf_resize_target: truncation not implemented\n");
703         return 0;
704     }
705     if (newsize == hfd->physsize) {
706         return 1;
707     }
708     /* Now, newsize must be larger than hfd->physsize, we seek to newsize - 1
709      * and write a single 0 byte to make the file exactly newsize bytes big. */
710     if (uae_fseeko64(hfd->handle->h, newsize - 1, SEEK_SET) != 0) {
711         uae_log("hdf_resize_target: fseek failed errno %d\n", errno);
712         return 0;
713     }
714     if (fwrite("", 1, 1, hfd->handle->h) != 1) {
715         uae_log("hdf_resize_target: failed to write byte at position "
716                 "%lld errno %d\n", newsize - 1, errno);
717         return 0;
718     }
719     uae_log("hdf_resize_target: %lld -> %lld\n", hfd->physsize, newsize);
720     hfd->physsize = newsize;
721     return 1;
722 }
723 
724 static int num_drives;
725 
hdf_init2(int force)726 static int hdf_init2 (int force)
727 {
728     static int done;
729 
730     if (done && !force)
731         return num_drives;
732     done = 1;
733     num_drives = 0;
734     return num_drives;
735 }
736 
hdf_init_target(void)737 int hdf_init_target (void) {
738     return hdf_init2 (0);
739 }
740 
hdf_getnumharddrives(void)741 int hdf_getnumharddrives (void)
742 {
743     return num_drives;
744 }
745 
hdf_getnameharddrive(int index,int flags,int * sectorsize,int * dangerousdrive)746 TCHAR *hdf_getnameharddrive (int index, int flags, int *sectorsize, int *dangerousdrive)
747 {
748     static char name[512];
749     char tmp[32];
750     uae_u64 size = uae_drives[index].size;
751     int nomedia = uae_drives[index].nomedia;
752     const char *dang = "?";
753     const char *rw = "RW";
754 
755     if (dangerousdrive)
756         *dangerousdrive = 0;
757     switch (uae_drives[index].dangerous)
758     {
759     case -5:
760         dang = "[PART]";
761         break;
762     case -6:
763         dang = "[MBR]";
764         break;
765     case -7:
766         dang = "[!]";
767         break;
768     case -8:
769         dang = "[UNK]";
770         break;
771     case -9:
772         dang = "[EMPTY]";
773         break;
774     case -3:
775         dang = "(CPRM)";
776         break;
777     case -2:
778         dang = "(SRAM)";
779         break;
780     case -1:
781         dang = "(RDB)";
782         break;
783     case 0:
784         dang = "[OS]";
785         if (dangerousdrive)
786             *dangerousdrive |= 1;
787         break;
788     }
789     if (nomedia) {
790         dang = "[NO MEDIA]";
791         if (dangerousdrive)
792             *dangerousdrive &= ~1;
793     }
794     if (uae_drives[index].readonly) {
795         rw = "RO";
796         if (dangerousdrive && !nomedia)
797             *dangerousdrive |= 2;
798     }
799 
800     if (sectorsize)
801         *sectorsize = uae_drives[index].bytespersector;
802     if (flags & 1) {
803         if (nomedia) {
804             _tcscpy (tmp, "N/A");
805         } else {
806             if (size >= 1024 * 1024 * 1024)
807                 _stprintf (tmp, "%.1fG", ((double)(uae_u32)(size / (1024 * 1024))) / 1024.0);
808             else if (size < 10 * 1024 * 1024)
809                 _stprintf (tmp, "%dK", (int) (size / 1024));
810             else
811                 _stprintf (tmp, "%.1fM", ((double)(uae_u32)(size / (1024))) / 1024.0);
812         }
813         _stprintf (name, "%10s [%s,%s] %s", dang, tmp, rw, uae_drives[index].device_name + 3);
814         return name;
815     }
816     if (flags & 2)
817         return uae_drives[index].device_path;
818     return uae_drives[index].device_name;
819 }
820