ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.29
Committed: 2006-05-08T12:15:58Z (18 years, 6 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.28: +273 -75 lines
Log Message:
Rewrite MacOS X CD-ROM support.

Other bugs fixed:
- CD-ROM media are polled and now can be changed without rebooting
- Buffer overflow, memory leak and extra wait in CD-ROM ejection code

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * sys_unix.cpp - System dependent routines, Unix implementation
3     *
4 gbeauche 1.24 * Basilisk II (C) 1997-2005 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22    
23     #include <sys/ioctl.h>
24     #include <sys/stat.h>
25     #include <errno.h>
26    
27 gbeauche 1.23 #ifdef HAVE_AVAILABILITYMACROS_H
28     #include <AvailabilityMacros.h>
29     #endif
30    
31 cebix 1.1 #ifdef __linux__
32 cebix 1.6 #include <sys/mount.h>
33 cebix 1.1 #include <linux/cdrom.h>
34     #include <linux/fd.h>
35     #include <linux/major.h>
36     #include <linux/kdev_t.h>
37 cebix 1.16 #include <dirent.h>
38 cebix 1.1 #endif
39    
40 cebix 1.3 #if defined(__FreeBSD__) || defined(__NetBSD__)
41 cebix 1.1 #include <sys/cdio.h>
42     #endif
43    
44 gbeauche 1.29 #if defined __APPLE__ && defined __MACH__
45     #include <sys/disk.h>
46     #if (defined AQUA || defined HAVE_FRAMEWORK_COREFOUNDATION)
47     #ifndef __MACOSX__
48     #define __MACOSX__ MAC_OS_X_VERSION_MIN_REQUIRED
49     #endif
50     #endif
51     #endif
52    
53 cebix 1.1 #include "main.h"
54     #include "macos_util.h"
55     #include "prefs.h"
56     #include "user_strings.h"
57     #include "sys.h"
58    
59     #define DEBUG 0
60     #include "debug.h"
61    
62    
63     // File handles are pointers to these structures
64     struct file_handle {
65 cebix 1.28 char *name; // Copy of device/file name
66 cebix 1.1 int fd;
67     bool is_file; // Flag: plain file or /dev/something?
68     bool is_floppy; // Flag: floppy device
69     bool is_cdrom; // Flag: CD-ROM device
70     bool read_only; // Copy of Sys_open() flag
71     loff_t start_byte; // Size of file header (if any)
72     loff_t file_size; // Size of file data (only valid if is_file is true)
73 gbeauche 1.29 bool is_media_present; // Flag: media is inserted and available
74 cebix 1.1
75     #if defined(__linux__)
76     int cdrom_cap; // CD-ROM capability flags (only valid if is_cdrom is true)
77     #elif defined(__FreeBSD__)
78     struct ioc_capability cdrom_cap;
79 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
80     char *ioctl_name; // For CDs on OS X - a device for special ioctls
81     int ioctl_fd;
82 cebix 1.1 #endif
83     };
84    
85 gbeauche 1.29 // Open file handles
86     struct open_file_handle {
87     file_handle *fh;
88     open_file_handle *next;
89     };
90     static open_file_handle *open_file_handles = NULL;
91    
92 cebix 1.1 // File handle of first floppy drive (for SysMountFirstFloppy())
93     static file_handle *first_floppy = NULL;
94    
95 gbeauche 1.29 // Prototypes
96     static void cdrom_close(file_handle *fh);
97     static bool cdrom_open(file_handle *fh, const char *path = NULL);
98    
99 cebix 1.1
100     /*
101     * Initialization
102     */
103    
104     void SysInit(void)
105     {
106 gbeauche 1.29 #if defined __MACOSX__
107     extern void DarwinSysInit(void);
108     DarwinSysInit();
109     #endif
110 cebix 1.1 }
111    
112    
113     /*
114     * Deinitialization
115     */
116    
117     void SysExit(void)
118     {
119 gbeauche 1.29 #if defined __MACOSX__
120     extern void DarwinSysExit(void);
121     DarwinSysExit();
122     #endif
123     }
124    
125    
126     /*
127     * Manage open file handles
128     */
129    
130     static void sys_add_file_handle(file_handle *fh)
131     {
132     open_file_handle *p = new open_file_handle;
133     p->fh = fh;
134     p->next = open_file_handles;
135     open_file_handles = p;
136     }
137    
138     static void sys_remove_file_handle(file_handle *fh)
139     {
140     open_file_handle *p = open_file_handles;
141     open_file_handle *q = NULL;
142    
143     while (p) {
144     if (p->fh == fh) {
145     if (q)
146     q->next = p->next;
147     else
148     open_file_handles = p->next;
149     delete p;
150     break;
151     }
152     q = p;
153     p = p->next;
154     }
155     }
156    
157    
158     /*
159     * Account for media that has just arrived
160     */
161    
162     void SysMediaArrived(const char *path, int type)
163     {
164     // Replace the "cdrom" entry (we are polling, it's unique)
165     if (type == MEDIA_CD && !PrefsFindBool("nocdrom"))
166     PrefsReplaceString("cdrom", path);
167    
168     // Wait for media to be available for reading
169     if (open_file_handles) {
170     const int MAX_WAIT = 5;
171     for (int i = 0; i < MAX_WAIT; i++) {
172     if (access(path, R_OK) == 0)
173     break;
174     switch (errno) {
175     case ENOENT: // Unlikely
176     case EACCES: // MacOS X is mounting the media
177     sleep(1);
178     continue;
179     }
180     printf("WARNING: Cannot access %s (%s)\n", path, strerror(errno));
181     return;
182     }
183     }
184    
185     for (open_file_handle *p = open_file_handles; p != NULL; p = p->next) {
186     file_handle * const fh = p->fh;
187    
188     // Re-open CD-ROM device
189     if (fh->is_cdrom && type == MEDIA_CD) {
190     cdrom_close(fh);
191     if (cdrom_open(fh, path)) {
192     fh->is_media_present = true;
193     MountVolume(fh);
194     }
195     }
196     }
197     }
198    
199    
200     /*
201     * Account for media that has just been removed
202     */
203    
204     void SysMediaRemoved(const char *path, int type)
205     {
206     if ((type & MEDIA_REMOVABLE) != MEDIA_CD)
207     return;
208    
209     for (open_file_handle *p = open_file_handles; p != NULL; p = p->next) {
210     file_handle * const fh = p->fh;
211    
212     // Mark media as not available
213     if (!fh->is_cdrom || !fh->is_media_present)
214     continue;
215     if (fh->name && strcmp(fh->name, path) == 0) {
216     fh->is_media_present = false;
217     break;
218     }
219     #if defined __MACOSX__
220     if (fh->ioctl_name && strcmp(fh->ioctl_name, path) == 0) {
221     fh->is_media_present = false;
222     break;
223     }
224     #endif
225     }
226 cebix 1.1 }
227    
228    
229     /*
230     * Mount first floppy disk
231     */
232    
233     void SysMountFirstFloppy(void)
234     {
235     if (first_floppy)
236     MountVolume(first_floppy);
237     }
238    
239    
240     /*
241     * This gets called when no "floppy" prefs items are found
242     * It scans for available floppy drives and adds appropriate prefs items
243     */
244    
245     void SysAddFloppyPrefs(void)
246     {
247     #if defined(__linux__)
248 cebix 1.27 DIR *fd_dir = opendir("/dev/floppy");
249     if (fd_dir) {
250     struct dirent *floppy_dev;
251     while ((floppy_dev = readdir(fd_dir)) != NULL) {
252     if (strstr(floppy_dev->d_name, "u1440") != NULL) {
253     char fd_dev[20];
254     sprintf(fd_dev, "/dev/floppy/%s", floppy_dev->d_name);
255     PrefsAddString("floppy", fd_dev);
256 cebix 1.16 }
257     }
258 cebix 1.27 closedir(fd_dir);
259     } else {
260     PrefsAddString("floppy", "/dev/fd0");
261     PrefsAddString("floppy", "/dev/fd1");
262 cebix 1.16 }
263 cebix 1.3 #elif defined(__NetBSD__)
264     PrefsAddString("floppy", "/dev/fd0a");
265     PrefsAddString("floppy", "/dev/fd1a");
266 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
267 gbeauche 1.25 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
268 nigel 1.22 extern void DarwinAddFloppyPrefs(void);
269    
270     DarwinAddFloppyPrefs();
271     #else
272     // Until I can convince the other guys that my Darwin code is useful,
273     // we just add something safe (a non-existant device):
274     PrefsAddString("floppy", "/dev/null");
275     #endif
276 cebix 1.1 #else
277     PrefsAddString("floppy", "/dev/fd0");
278     PrefsAddString("floppy", "/dev/fd1");
279     #endif
280     }
281    
282    
283     /*
284     * This gets called when no "disk" prefs items are found
285     * It scans for available HFS volumes and adds appropriate prefs items
286 nigel 1.19 * On OS X, we could do the same, but on an OS X machine I think it is
287     * very unlikely that any mounted volumes would contain a system which
288     * is old enough to boot a 68k Mac, so we just do nothing here for now.
289 cebix 1.1 */
290    
291     void SysAddDiskPrefs(void)
292     {
293     #ifdef __linux__
294     FILE *f = fopen("/etc/fstab", "r");
295     if (f) {
296     char line[256];
297     while(fgets(line, 255, f)) {
298     // Read line
299     int len = strlen(line);
300 cebix 1.10 if (len == 0 || line[0] == '#')
301 cebix 1.1 continue;
302     line[len-1] = 0;
303    
304     // Parse line
305     char *dev, *mnt_point, *fstype;
306 cebix 1.3 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
307 cebix 1.1 if (strcmp(fstype, "hfs") == 0)
308     PrefsAddString("disk", dev);
309     }
310 cebix 1.3 free(dev); free(mnt_point); free(fstype);
311 cebix 1.1 }
312     fclose(f);
313     }
314     #endif
315     }
316    
317    
318     /*
319     * This gets called when no "cdrom" prefs items are found
320     * It scans for available CD-ROM drives and adds appropriate prefs items
321     */
322    
323     void SysAddCDROMPrefs(void)
324     {
325     // Don't scan for drives if nocdrom option given
326     if (PrefsFindBool("nocdrom"))
327     return;
328    
329     #if defined(__linux__)
330 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0)
331 cebix 1.16 PrefsAddString("cdrom", "/dev/cdrom");
332     else {
333     DIR *cd_dir = opendir("/dev/cdroms");
334     if (cd_dir) {
335     struct dirent *cdrom_dev;
336     while ((cdrom_dev = readdir(cd_dir)) != NULL) {
337     if (strcmp(cdrom_dev->d_name, ".") != 0 && strcmp(cdrom_dev->d_name, "..") != 0) {
338 gbeauche 1.17 char cd_dev[20];
339     sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
340 cebix 1.16 PrefsAddString("cdrom", cd_dev);
341     }
342     }
343     closedir(cd_dir);
344     }
345     }
346 gbeauche 1.29 #elif defined __MACOSX__
347     // There is no predefined path for CD-ROMs on MacOS X. Rather, we
348     // define a single fake CD-ROM entry for the emulated MacOS.
349     // XXX this means we handle only CD-ROM drive at a time, wherever
350     // the disk is, the latest one is used.
351     PrefsAddString("cdrom", "/dev/poll/cdrom");
352 cebix 1.18 #elif defined(__FreeBSD__) || defined(__NetBSD__)
353 cebix 1.1 PrefsAddString("cdrom", "/dev/cd0c");
354     #endif
355     }
356    
357    
358     /*
359     * Add default serial prefs (must be added, even if no ports present)
360     */
361    
362     void SysAddSerialPrefs(void)
363     {
364     #if defined(__linux__)
365 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0) {
366 cebix 1.16 PrefsAddString("seriala", "/dev/ttyS0");
367     PrefsAddString("serialb", "/dev/ttyS1");
368     } else {
369     PrefsAddString("seriala", "/dev/tts/0");
370     PrefsAddString("serialb", "/dev/tts/1");
371     }
372 cebix 1.1 #elif defined(__FreeBSD__)
373     PrefsAddString("seriala", "/dev/cuaa0");
374     PrefsAddString("serialb", "/dev/cuaa1");
375 cebix 1.3 #elif defined(__NetBSD__)
376     PrefsAddString("seriala", "/dev/tty00");
377     PrefsAddString("serialb", "/dev/tty01");
378 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
379 gbeauche 1.25 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
380 nigel 1.22 extern void DarwinAddSerialPrefs(void);
381    
382     DarwinAddSerialPrefs();
383     #else
384     // Until I can convince the other guys that my Darwin code is useful,
385     // we just add something safe (non-existant devices):
386     PrefsAddString("seriala", "/dev/null");
387     PrefsAddString("serialb", "/dev/null");
388     #endif
389 cebix 1.1 #endif
390     }
391    
392    
393     /*
394 gbeauche 1.29 * Open CD-ROM device and initialize internal data
395     */
396    
397     static bool cdrom_open_1(file_handle *fh)
398     {
399     #if defined __MACOSX__
400     // In OS X, the device name is OK for sending ioctls to,
401     // but not for reading raw CDROM data from.
402     // (it seems to have extra data padded in)
403     //
404     // So, we keep the already opened file handle,
405     // and open a slightly different file for CDROM data
406     //
407     fh->ioctl_fd = fh->fd;
408     fh->ioctl_name = fh->name;
409     fh->fd = -1;
410     fh->name = (char *)malloc(strlen(fh->ioctl_name) + 3);
411     if (fh->name) {
412     strcpy(fh->name, fh->ioctl_name);
413     strcat(fh->name, "s1");
414     fh->fd = open(fh->name, O_RDONLY, O_NONBLOCK);
415     }
416     if (fh->ioctl_fd < 0)
417     return false;
418     #endif
419     return true;
420     }
421    
422     bool cdrom_open(file_handle *fh, const char *path)
423     {
424     if (path)
425     fh->name = strdup(path);
426     fh->fd = open(fh->name, O_RDONLY, O_NONBLOCK);
427     fh->start_byte = 0;
428     if (!cdrom_open_1(fh))
429     return false;
430     return fh->fd >= 0;
431     }
432    
433    
434     /*
435     * Close a CD-ROM device
436     */
437    
438     void cdrom_close(file_handle *fh)
439     {
440     if (fh->fd >= 0) {
441     close(fh->fd);
442     fh->fd = -1;
443     }
444     if (fh->name) {
445     free(fh->name);
446     fh->name = NULL;
447     }
448     #if defined __MACOSX__
449     if (fh->ioctl_fd >= 0) {
450     close(fh->ioctl_fd);
451     fh->ioctl_fd = -1;
452     }
453     if (fh->ioctl_name) {
454     free(fh->ioctl_name);
455     fh->ioctl_name = NULL;
456     }
457     #endif
458     }
459    
460    
461     /*
462 cebix 1.1 * Check if device is a mounted HFS volume, get mount name
463     */
464    
465     static bool is_drive_mounted(const char *dev_name, char *mount_name)
466     {
467     #ifdef __linux__
468     FILE *f = fopen("/proc/mounts", "r");
469     if (f) {
470     char line[256];
471     while(fgets(line, 255, f)) {
472     // Read line
473     int len = strlen(line);
474     if (len == 0)
475     continue;
476     line[len-1] = 0;
477    
478     // Parse line
479     if (strncmp(line, dev_name, strlen(dev_name)) == 0) {
480     mount_name[0] = 0;
481 cebix 1.3 char *dummy;
482     sscanf(line, "%as %s", &dummy, mount_name);
483     free(dummy);
484 cebix 1.1 fclose(f);
485     return true;
486     }
487     }
488     fclose(f);
489     }
490     #endif
491     return false;
492     }
493    
494    
495     /*
496     * Open file/device, create new file handle (returns NULL on error)
497     */
498    
499     void *Sys_open(const char *name, bool read_only)
500     {
501     bool is_file = strncmp(name, "/dev/", 5) != 0;
502 cebix 1.3 #if defined(__FreeBSD__)
503 cebix 1.1 // SCSI IDE
504     bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0 || strncmp(name, "/dev/acd", 8) == 0;
505     #else
506     bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0;
507     #endif
508 cebix 1.28 bool is_floppy = strncmp(name, "/dev/fd", 7) == 0;
509 cebix 1.1
510 gbeauche 1.29 bool is_polled_media = strncmp(name, "/dev/poll/", 10) == 0;
511     if (is_floppy) // Floppy open fails if there's no disk inserted
512     is_polled_media = true;
513    
514     #if defined __MACOSX__
515 nigel 1.19 // There is no set filename in /dev which is the cdrom,
516     // so we have to see if it is any of the devices that we found earlier
517     {
518 gbeauche 1.29 int index = 0;
519     const char *str;
520     while ((str = PrefsFindString("cdrom", index++)) != NULL) {
521     if (is_polled_media || strcmp(str, name) == 0) {
522     is_cdrom = true;
523     read_only = true;
524     break;
525     }
526 nigel 1.19 }
527     }
528     #endif
529    
530 cebix 1.1 D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
531    
532     // Check if write access is allowed, set read-only flag if not
533     if (!read_only && access(name, W_OK))
534     read_only = true;
535    
536     // Print warning message and eventually unmount drive when this is an HFS volume mounted under Linux (double mounting will corrupt the volume)
537     char mount_name[256];
538     if (!is_file && !read_only && is_drive_mounted(name, mount_name)) {
539     char str[512];
540     sprintf(str, GetString(STR_VOLUME_IS_MOUNTED_WARN), mount_name);
541     WarningAlert(str);
542     sprintf(str, "umount %s", mount_name);
543     if (system(str)) {
544     sprintf(str, GetString(STR_CANNOT_UNMOUNT_WARN), mount_name, strerror(errno));
545     WarningAlert(str);
546     return NULL;
547     }
548     }
549    
550     // Open file/device
551 gbeauche 1.29 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__)
552 cebix 1.1 int fd = open(name, (read_only ? O_RDONLY : O_RDWR) | (is_cdrom ? O_NONBLOCK : 0));
553     #else
554     int fd = open(name, read_only ? O_RDONLY : O_RDWR);
555     #endif
556     if (fd < 0 && !read_only) {
557     // Read-write failed, try read-only
558     read_only = true;
559     fd = open(name, O_RDONLY);
560     }
561 gbeauche 1.29 if (fd >= 0 || is_polled_media) {
562 cebix 1.1 file_handle *fh = new file_handle;
563     fh->name = strdup(name);
564     fh->fd = fd;
565     fh->is_file = is_file;
566     fh->read_only = read_only;
567     fh->start_byte = 0;
568 cebix 1.28 fh->is_floppy = is_floppy;
569     fh->is_cdrom = is_cdrom;
570 gbeauche 1.29 fh->is_media_present = false;
571     #if defined __MACOSX__
572     fh->ioctl_fd = -1;
573     fh->ioctl_name = NULL;
574     #endif
575 cebix 1.1 if (fh->is_file) {
576 gbeauche 1.29 fh->is_media_present = true;
577 cebix 1.1 // Detect disk image file layout
578     loff_t size = 0;
579     size = lseek(fd, 0, SEEK_END);
580     uint8 data[256];
581     lseek(fd, 0, SEEK_SET);
582     read(fd, data, 256);
583     FileDiskLayout(size, data, fh->start_byte, fh->file_size);
584     } else {
585     struct stat st;
586     if (fstat(fd, &st) == 0) {
587 gbeauche 1.29 fh->is_media_present = true;
588 cebix 1.1 if (S_ISBLK(st.st_mode)) {
589     fh->is_cdrom = is_cdrom;
590     #if defined(__linux__)
591     fh->is_floppy = (MAJOR(st.st_rdev) == FLOPPY_MAJOR);
592     #ifdef CDROM_GET_CAPABILITY
593     if (is_cdrom) {
594     fh->cdrom_cap = ioctl(fh->fd, CDROM_GET_CAPABILITY);
595     if (fh->cdrom_cap < 0)
596     fh->cdrom_cap = 0;
597     }
598     #else
599     fh->cdrom_cap = 0;
600     #endif
601 cebix 1.4 #elif defined(__FreeBSD__)
602 cebix 1.1 fh->is_floppy = ((st.st_rdev >> 16) == 2);
603     #ifdef CDIOCCAPABILITY
604     if (is_cdrom) {
605     if (ioctl(fh->fd, CDIOCCAPABILITY, &fh->cdrom_cap) < 0)
606     memset(&fh->cdrom_cap, 0, sizeof(fh->cdrom_cap));
607     }
608     #else
609     fh->cdrom_cap = 0;
610     #endif
611 cebix 1.4 #elif defined(__NetBSD__)
612     fh->is_floppy = ((st.st_rdev >> 16) == 2);
613 cebix 1.1 #endif
614     }
615 gbeauche 1.29 #if defined __MACOSX__
616     if (is_cdrom) {
617     fh->is_cdrom = true;
618     fh->is_floppy = false;
619     if (cdrom_open_1(fh))
620     fh->is_media_present = true;
621 nigel 1.19 }
622     #endif
623 cebix 1.1 }
624     }
625     if (fh->is_floppy && first_floppy == NULL)
626     first_floppy = fh;
627 gbeauche 1.29 sys_add_file_handle(fh);
628 cebix 1.1 return fh;
629     } else {
630     printf("WARNING: Cannot open %s (%s)\n", name, strerror(errno));
631     return NULL;
632     }
633     }
634    
635    
636     /*
637     * Close file/device, delete file handle
638     */
639    
640     void Sys_close(void *arg)
641     {
642     file_handle *fh = (file_handle *)arg;
643     if (!fh)
644     return;
645    
646 gbeauche 1.29 sys_remove_file_handle(fh);
647    
648     if (fh->is_cdrom)
649     cdrom_close(fh);
650 cebix 1.28 if (fh->fd >= 0)
651     close(fh->fd);
652 cebix 1.1 if (fh->name)
653     free(fh->name);
654     delete fh;
655     }
656    
657    
658     /*
659     * Read "length" bytes from file/device, starting at "offset", to "buffer",
660     * returns number of bytes read (or 0)
661     */
662    
663     size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
664     {
665     file_handle *fh = (file_handle *)arg;
666     if (!fh)
667     return 0;
668    
669     // Seek to position
670     if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
671     return 0;
672    
673     // Read data
674     return read(fh->fd, buffer, length);
675     }
676    
677    
678     /*
679     * Write "length" bytes from "buffer" to file/device, starting at "offset",
680     * returns number of bytes written (or 0)
681     */
682    
683     size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
684     {
685     file_handle *fh = (file_handle *)arg;
686     if (!fh)
687     return 0;
688    
689     // Seek to position
690     if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
691     return 0;
692    
693     // Write data
694     return write(fh->fd, buffer, length);
695     }
696    
697    
698     /*
699     * Return size of file/device (minus header)
700     */
701    
702     loff_t SysGetFileSize(void *arg)
703     {
704     file_handle *fh = (file_handle *)arg;
705     if (!fh)
706     return true;
707    
708     if (fh->is_file)
709     return fh->file_size;
710     else {
711 cebix 1.3 #if defined(__linux__)
712 cebix 1.5 long blocks;
713     if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0)
714     return 0;
715     D(bug(" BLKGETSIZE returns %d blocks\n", blocks));
716     return (loff_t)blocks * 512;
717 gbeauche 1.29 #elif defined __MACOSX__
718     uint32 block_size;
719     if (ioctl(fh->ioctl_fd, DKIOCGETBLOCKSIZE, &block_size) < 0)
720     return 0;
721     D(bug(" DKIOCGETBLOCKSIZE returns %lu bytes\n", (unsigned long)block_size));
722     uint64 block_count;
723     if (ioctl(fh->ioctl_fd, DKIOCGETBLOCKCOUNT, &block_count) < 0)
724     return 0;
725     D(bug(" DKIOCGETBLOCKCOUNT returns %llu blocks\n", (unsigned long long)block_count));
726     return block_count * block_size;
727 cebix 1.1 #else
728     return lseek(fh->fd, 0, SEEK_END) - fh->start_byte;
729     #endif
730     }
731     }
732    
733    
734     /*
735     * Eject volume (if applicable)
736     */
737    
738     void SysEject(void *arg)
739     {
740     file_handle *fh = (file_handle *)arg;
741     if (!fh)
742     return;
743    
744     #if defined(__linux__)
745     if (fh->is_floppy) {
746 cebix 1.28 if (fh->fd >= 0) {
747     fsync(fh->fd);
748     ioctl(fh->fd, FDFLUSH);
749     ioctl(fh->fd, FDEJECT);
750     close(fh->fd); // Close and reopen so the driver will see the media change
751     }
752 cebix 1.14 fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
753 cebix 1.1 } else if (fh->is_cdrom) {
754     ioctl(fh->fd, CDROMEJECT);
755     close(fh->fd); // Close and reopen so the driver will see the media change
756     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
757     }
758 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
759 cebix 1.1 if (fh->is_floppy) {
760     fsync(fh->fd);
761     } else if (fh->is_cdrom) {
762     ioctl(fh->fd, CDIOCEJECT);
763     close(fh->fd); // Close and reopen so the driver will see the media change
764     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
765     }
766 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
767 gbeauche 1.29 if (fh->is_cdrom && fh->is_media_present) {
768 nigel 1.19 close(fh->fd);
769 gbeauche 1.29 fh->fd = -1;
770     if (ioctl(fh->ioctl_fd, DKIOCEJECT) < 0) {
771     D(bug(" DKIOCEJECT failed on file %s: %s\n",
772     fh->ioctl_name, strerror(errno)));
773 nigel 1.19
774 gbeauche 1.29 // If we are running MacOS X, the device may be in busy
775     // state because the Finder has mounted the disk
776     close(fh->ioctl_fd);
777     fh->ioctl_fd = -1;
778 nigel 1.19
779 gbeauche 1.29 // Try to use "diskutil eject" but it can take up to 5
780     // seconds to complete
781     static const char eject_cmd[] = "/usr/sbin/diskutil eject %s 2>&1 >/dev/null";
782     char *cmd = (char *)alloca(strlen(eject_cmd) + strlen(fh->ioctl_name) + 1);
783     sprintf(cmd, eject_cmd, fh->ioctl_name);
784 nigel 1.19 system(cmd);
785     }
786 gbeauche 1.29 fh->is_media_present = false;
787 nigel 1.19 }
788 cebix 1.1 #endif
789     }
790    
791    
792     /*
793     * Format volume (if applicable)
794     */
795    
796     bool SysFormat(void *arg)
797     {
798     file_handle *fh = (file_handle *)arg;
799     if (!fh)
800     return false;
801    
802     //!!
803     return true;
804     }
805    
806    
807     /*
808     * Check if file/device is read-only (this includes the read-only flag on Sys_open())
809     */
810    
811     bool SysIsReadOnly(void *arg)
812     {
813     file_handle *fh = (file_handle *)arg;
814     if (!fh)
815     return true;
816    
817 cebix 1.3 #if defined(__linux__)
818 cebix 1.1 if (fh->is_floppy) {
819 cebix 1.28 if (fh->fd >= 0) {
820     struct floppy_drive_struct stat;
821     ioctl(fh->fd, FDGETDRVSTAT, &stat);
822     return !(stat.flags & FD_DISK_WRITABLE);
823     } else
824     return true;
825 cebix 1.1 } else
826     #endif
827     return fh->read_only;
828     }
829    
830    
831     /*
832     * Check if the given file handle refers to a fixed or a removable disk
833     */
834    
835     bool SysIsFixedDisk(void *arg)
836     {
837     file_handle *fh = (file_handle *)arg;
838     if (!fh)
839     return true;
840    
841     if (fh->is_file)
842     return true;
843     else if (fh->is_floppy || fh->is_cdrom)
844     return false;
845     else
846     return true;
847     }
848    
849    
850     /*
851     * Check if a disk is inserted in the drive (always true for files)
852     */
853    
854     bool SysIsDiskInserted(void *arg)
855     {
856     file_handle *fh = (file_handle *)arg;
857     if (!fh)
858     return false;
859    
860     if (fh->is_file) {
861     return true;
862    
863     #if defined(__linux__)
864     } else if (fh->is_floppy) {
865     char block[512];
866     lseek(fh->fd, 0, SEEK_SET);
867 cebix 1.14 ssize_t actual = read(fh->fd, block, 512);
868     if (actual < 0) {
869     close(fh->fd); // Close and reopen so the driver will see the media change
870     fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
871     actual = read(fh->fd, block, 512);
872     }
873     return actual == 512;
874 cebix 1.1 } else if (fh->is_cdrom) {
875 cebix 1.8 #ifdef CDROM_MEDIA_CHANGED
876     if (fh->cdrom_cap & CDC_MEDIA_CHANGED) {
877     // If we don't do this, all attempts to read from a disc fail
878 cebix 1.14 // once the tray has been opened (altough the TOC reads fine).
879 cebix 1.8 // Can somebody explain this to me?
880     if (ioctl(fh->fd, CDROM_MEDIA_CHANGED) == 1) {
881     close(fh->fd);
882     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
883     }
884     }
885     #endif
886 cebix 1.1 #ifdef CDROM_DRIVE_STATUS
887     if (fh->cdrom_cap & CDC_DRIVE_STATUS) {
888     return ioctl(fh->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
889     }
890     #endif
891     cdrom_tochdr header;
892     return ioctl(fh->fd, CDROMREADTOCHDR, &header) == 0;
893 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
894 cebix 1.1 } else if (fh->is_floppy) {
895     return false; //!!
896     } else if (fh->is_cdrom) {
897     struct ioc_toc_header header;
898     return ioctl(fh->fd, CDIOREADTOCHEADER, &header) == 0;
899 gbeauche 1.29 #elif defined __MACOSX__
900     } else if (fh->is_cdrom || fh->is_floppy) {
901     return fh->is_media_present;
902 cebix 1.1 #endif
903    
904     } else
905     return true;
906     }
907    
908    
909     /*
910     * Prevent medium removal (if applicable)
911     */
912    
913     void SysPreventRemoval(void *arg)
914     {
915     file_handle *fh = (file_handle *)arg;
916     if (!fh)
917     return;
918    
919     #if defined(__linux__) && defined(CDROM_LOCKDOOR)
920     if (fh->is_cdrom)
921     ioctl(fh->fd, CDROM_LOCKDOOR, 1);
922     #endif
923     }
924    
925    
926     /*
927     * Allow medium removal (if applicable)
928     */
929    
930     void SysAllowRemoval(void *arg)
931     {
932     file_handle *fh = (file_handle *)arg;
933     if (!fh)
934     return;
935    
936 cebix 1.2 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
937 cebix 1.1 if (fh->is_cdrom)
938     ioctl(fh->fd, CDROM_LOCKDOOR, 0);
939     #endif
940     }
941    
942    
943     /*
944     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
945     */
946    
947     bool SysCDReadTOC(void *arg, uint8 *toc)
948     {
949     file_handle *fh = (file_handle *)arg;
950     if (!fh)
951     return false;
952    
953     if (fh->is_cdrom) {
954     #if defined(__linux__)
955     uint8 *p = toc + 2;
956    
957     // Header
958     cdrom_tochdr header;
959     if (ioctl(fh->fd, CDROMREADTOCHDR, &header) < 0)
960     return false;
961     *p++ = header.cdth_trk0;
962     *p++ = header.cdth_trk1;
963    
964     // Tracks
965     cdrom_tocentry entry;
966     for (int i=header.cdth_trk0; i<=header.cdth_trk1; i++) {
967     entry.cdte_track = i;
968     entry.cdte_format = CDROM_MSF;
969     if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
970     return false;
971     *p++ = 0;
972     *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
973     *p++ = entry.cdte_track;
974     *p++ = 0;
975     *p++ = 0;
976     *p++ = entry.cdte_addr.msf.minute;
977     *p++ = entry.cdte_addr.msf.second;
978     *p++ = entry.cdte_addr.msf.frame;
979     }
980    
981     // Leadout track
982     entry.cdte_track = CDROM_LEADOUT;
983     entry.cdte_format = CDROM_MSF;
984     if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
985     return false;
986     *p++ = 0;
987     *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
988     *p++ = entry.cdte_track;
989     *p++ = 0;
990     *p++ = 0;
991     *p++ = entry.cdte_addr.msf.minute;
992     *p++ = entry.cdte_addr.msf.second;
993     *p++ = entry.cdte_addr.msf.frame;
994    
995     // TOC size
996     int toc_size = p - toc;
997     *toc++ = toc_size >> 8;
998     *toc++ = toc_size & 0xff;
999     return true;
1000 gbeauche 1.29 #elif defined __MACOSX__ && defined MAC_OS_X_VERSION_10_2
1001     if (fh->is_media_present) {
1002     extern bool DarwinCDReadTOC(char *name, uint8 *toc);
1003     return DarwinCDReadTOC(fh->name, toc);
1004     }
1005     return false;
1006 cebix 1.4 #elif defined(__FreeBSD__)
1007 cebix 1.1 uint8 *p = toc + 2;
1008    
1009     // Header
1010     struct ioc_toc_header header;
1011     if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
1012     return false;
1013     *p++ = header.starting_track;
1014     *p++ = header.ending_track;
1015    
1016     // Tracks
1017     struct ioc_read_toc_single_entry entry;
1018     for (int i=header.starting_track; i<=header.ending_track; i++) {
1019     entry.track = i;
1020     entry.address_format = CD_MSF_FORMAT;
1021     if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
1022     return false;
1023     *p++ = 0;
1024     *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
1025     *p++ = entry.entry.track;
1026     *p++ = 0;
1027     *p++ = 0;
1028     *p++ = entry.entry.addr.msf.minute;
1029     *p++ = entry.entry.addr.msf.second;
1030     *p++ = entry.entry.addr.msf.frame;
1031     }
1032    
1033     // Leadout track
1034     entry.track = CD_TRACK_INFO;
1035     entry.address_format = CD_MSF_FORMAT;
1036     if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
1037     return false;
1038     *p++ = 0;
1039     *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
1040     *p++ = entry.entry.track;
1041     *p++ = 0;
1042     *p++ = 0;
1043     *p++ = entry.entry.addr.msf.minute;
1044     *p++ = entry.entry.addr.msf.second;
1045     *p++ = entry.entry.addr.msf.frame;
1046 cebix 1.4
1047     // TOC size
1048     int toc_size = p - toc;
1049     *toc++ = toc_size >> 8;
1050     *toc++ = toc_size & 0xff;
1051     return true;
1052     #elif defined(__NetBSD__)
1053     uint8 *p = toc + 2;
1054    
1055     // Header
1056     struct ioc_toc_header header;
1057     if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
1058     return false;
1059     *p++ = header.starting_track;
1060     *p++ = header.ending_track;
1061    
1062     // Tracks (this is nice... :-)
1063     struct ioc_read_toc_entry entries;
1064     entries.address_format = CD_MSF_FORMAT;
1065     entries.starting_track = 1;
1066     entries.data_len = 800;
1067     entries.data = (cd_toc_entry *)p;
1068     if (ioctl(fh->fd, CDIOREADTOCENTRIES, &entries) < 0)
1069     return false;
1070 cebix 1.1
1071     // TOC size
1072     int toc_size = p - toc;
1073     *toc++ = toc_size >> 8;
1074     *toc++ = toc_size & 0xff;
1075     return true;
1076 cebix 1.15 #else
1077     return false;
1078 cebix 1.1 #endif
1079     } else
1080     return false;
1081     }
1082    
1083    
1084     /*
1085     * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
1086     */
1087    
1088     bool SysCDGetPosition(void *arg, uint8 *pos)
1089     {
1090     file_handle *fh = (file_handle *)arg;
1091     if (!fh)
1092     return false;
1093    
1094     if (fh->is_cdrom) {
1095     #if defined(__linux__)
1096     cdrom_subchnl chan;
1097     chan.cdsc_format = CDROM_MSF;
1098     if (ioctl(fh->fd, CDROMSUBCHNL, &chan) < 0)
1099     return false;
1100     *pos++ = 0;
1101     *pos++ = chan.cdsc_audiostatus;
1102     *pos++ = 0;
1103     *pos++ = 12; // Sub-Q data length
1104     *pos++ = 0;
1105     *pos++ = (chan.cdsc_adr << 4) | chan.cdsc_ctrl;
1106     *pos++ = chan.cdsc_trk;
1107     *pos++ = chan.cdsc_ind;
1108     *pos++ = 0;
1109     *pos++ = chan.cdsc_absaddr.msf.minute;
1110     *pos++ = chan.cdsc_absaddr.msf.second;
1111     *pos++ = chan.cdsc_absaddr.msf.frame;
1112     *pos++ = 0;
1113     *pos++ = chan.cdsc_reladdr.msf.minute;
1114     *pos++ = chan.cdsc_reladdr.msf.second;
1115     *pos++ = chan.cdsc_reladdr.msf.frame;
1116     return true;
1117 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1118 cebix 1.1 struct ioc_read_subchannel chan;
1119     chan.data_format = CD_MSF_FORMAT;
1120     chan.address_format = CD_MSF_FORMAT;
1121     chan.track = CD_CURRENT_POSITION;
1122     if (ioctl(fh->fd, CDIOCREADSUBCHANNEL, &chan) < 0)
1123     return false;
1124     *pos++ = 0;
1125     *pos++ = chan.data->header.audio_status;
1126     *pos++ = 0;
1127     *pos++ = 12; // Sub-Q data length
1128     *pos++ = 0;
1129     *pos++ = (chan.data->what.position.addr_type << 4) | chan.data->what.position.control;
1130     *pos++ = chan.data->what.position.track_number;
1131     *pos++ = chan.data->what.position.index_number;
1132     *pos++ = 0;
1133     *pos++ = chan.data->what.position.absaddr.msf.minute;
1134     *pos++ = chan.data->what.position.absaddr.msf.second;
1135     *pos++ = chan.data->what.position.absaddr.msf.frame;
1136     *pos++ = 0;
1137     *pos++ = chan.data->what.position.reladdr.msf.minute;
1138     *pos++ = chan.data->what.position.reladdr.msf.second;
1139     *pos++ = chan.data->what.position.reladdr.msf.frame;
1140     return true;
1141 cebix 1.15 #else
1142     return false;
1143 cebix 1.1 #endif
1144     } else
1145     return false;
1146     }
1147    
1148    
1149     /*
1150     * Play CD audio
1151     */
1152    
1153     bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
1154     {
1155     file_handle *fh = (file_handle *)arg;
1156     if (!fh)
1157     return false;
1158    
1159     if (fh->is_cdrom) {
1160     #if defined(__linux__)
1161     cdrom_msf play;
1162     play.cdmsf_min0 = start_m;
1163     play.cdmsf_sec0 = start_s;
1164     play.cdmsf_frame0 = start_f;
1165     play.cdmsf_min1 = end_m;
1166     play.cdmsf_sec1 = end_s;
1167     play.cdmsf_frame1 = end_f;
1168     return ioctl(fh->fd, CDROMPLAYMSF, &play) == 0;
1169 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1170 cebix 1.1 struct ioc_play_msf play;
1171     play.start_m = start_m;
1172     play.start_s = start_s;
1173     play.start_f = start_f;
1174     play.end_m = end_m;
1175     play.end_s = end_s;
1176     play.end_f = end_f;
1177     return ioctl(fh->fd, CDIOCPLAYMSF, &play) == 0;
1178 cebix 1.15 #else
1179     return false;
1180 cebix 1.1 #endif
1181     } else
1182     return false;
1183     }
1184    
1185    
1186     /*
1187     * Pause CD audio
1188     */
1189    
1190     bool SysCDPause(void *arg)
1191     {
1192     file_handle *fh = (file_handle *)arg;
1193     if (!fh)
1194     return false;
1195    
1196     if (fh->is_cdrom) {
1197     #if defined(__linux__)
1198     return ioctl(fh->fd, CDROMPAUSE) == 0;
1199 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1200 cebix 1.1 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1201 cebix 1.15 #else
1202     return false;
1203 cebix 1.1 #endif
1204     } else
1205     return false;
1206     }
1207    
1208    
1209     /*
1210     * Resume paused CD audio
1211     */
1212    
1213     bool SysCDResume(void *arg)
1214     {
1215     file_handle *fh = (file_handle *)arg;
1216     if (!fh)
1217     return false;
1218    
1219     if (fh->is_cdrom) {
1220     #if defined(__linux__)
1221     return ioctl(fh->fd, CDROMRESUME) == 0;
1222 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1223 cebix 1.1 return ioctl(fh->fd, CDIOCRESUME) == 0;
1224 cebix 1.15 #else
1225     return false;
1226 cebix 1.1 #endif
1227     } else
1228     return false;
1229     }
1230    
1231    
1232     /*
1233     * Stop CD audio
1234     */
1235    
1236     bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
1237     {
1238     file_handle *fh = (file_handle *)arg;
1239     if (!fh)
1240     return false;
1241    
1242     if (fh->is_cdrom) {
1243     #if defined(__linux__)
1244     return ioctl(fh->fd, CDROMSTOP) == 0;
1245 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1246 cebix 1.1 return ioctl(fh->fd, CDIOCSTOP) == 0;
1247 cebix 1.15 #else
1248     return false;
1249 cebix 1.1 #endif
1250     } else
1251     return false;
1252     }
1253    
1254    
1255     /*
1256     * Perform CD audio fast-forward/fast-reverse operation starting from specified address
1257     */
1258    
1259     bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
1260     {
1261     file_handle *fh = (file_handle *)arg;
1262     if (!fh)
1263     return false;
1264    
1265     // Not supported under Linux
1266     return false;
1267     }
1268    
1269    
1270     /*
1271     * Set CD audio volume (0..255 each channel)
1272     */
1273    
1274     void SysCDSetVolume(void *arg, uint8 left, uint8 right)
1275     {
1276     file_handle *fh = (file_handle *)arg;
1277     if (!fh)
1278     return;
1279    
1280     if (fh->is_cdrom) {
1281     #if defined(__linux__)
1282     cdrom_volctrl vol;
1283     vol.channel0 = vol.channel2 = left;
1284     vol.channel1 = vol.channel3 = right;
1285     ioctl(fh->fd, CDROMVOLCTRL, &vol);
1286 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1287 cebix 1.1 struct ioc_vol vol;
1288     vol.vol[0] = vol.vol[2] = left;
1289     vol.vol[1] = vol.vol[3] = right;
1290     ioctl(fh->fd, CDIOCSETVOL, &vol);
1291     #endif
1292     }
1293     }
1294    
1295    
1296     /*
1297     * Get CD audio volume (0..255 each channel)
1298     */
1299    
1300     void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
1301     {
1302     file_handle *fh = (file_handle *)arg;
1303     if (!fh)
1304     return;
1305    
1306     left = right = 0;
1307     if (fh->is_cdrom) {
1308     #if defined(__linux__)
1309     cdrom_volctrl vol;
1310     ioctl(fh->fd, CDROMVOLREAD, &vol);
1311     left = vol.channel0;
1312     right = vol.channel1;
1313 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1314 cebix 1.1 struct ioc_vol vol;
1315     ioctl(fh->fd, CDIOCGETVOL, &vol);
1316     left = vol.vol[0];
1317     right = vol.vol[1];
1318     #endif
1319     }
1320     }