Commit 19cb37389f4641d48803f0c5d72708749cbcf318
1 parent
66c6ef76
better support of host drives
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2124 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
5 changed files
with
808 additions
and
164 deletions
block-raw.c
... | ... | @@ -46,100 +46,42 @@ |
46 | 46 | #ifdef __sun__ |
47 | 47 | #include <sys/dkio.h> |
48 | 48 | #endif |
49 | +#ifdef __linux__ | |
50 | +#include <sys/ioctl.h> | |
51 | +#include <linux/cdrom.h> | |
52 | +#include <linux/fd.h> | |
53 | +#endif | |
49 | 54 | |
50 | -typedef struct BDRVRawState { | |
51 | - int fd; | |
52 | -} BDRVRawState; | |
55 | +//#define DEBUG_FLOPPY | |
53 | 56 | |
54 | -#ifdef CONFIG_COCOA | |
55 | -static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); | |
56 | -static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); | |
57 | - | |
58 | -kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) | |
59 | -{ | |
60 | - kern_return_t kernResult; | |
61 | - mach_port_t masterPort; | |
62 | - CFMutableDictionaryRef classesToMatch; | |
63 | - | |
64 | - kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); | |
65 | - if ( KERN_SUCCESS != kernResult ) { | |
66 | - printf( "IOMasterPort returned %d\n", kernResult ); | |
67 | - } | |
68 | - | |
69 | - classesToMatch = IOServiceMatching( kIOCDMediaClass ); | |
70 | - if ( classesToMatch == NULL ) { | |
71 | - printf( "IOServiceMatching returned a NULL dictionary.\n" ); | |
72 | - } else { | |
73 | - CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); | |
74 | - } | |
75 | - kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); | |
76 | - if ( KERN_SUCCESS != kernResult ) | |
77 | - { | |
78 | - printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); | |
79 | - } | |
80 | - | |
81 | - return kernResult; | |
82 | -} | |
57 | +#define FTYPE_FILE 0 | |
58 | +#define FTYPE_CD 1 | |
59 | +#define FTYPE_FD 2 | |
83 | 60 | |
84 | -kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) | |
85 | -{ | |
86 | - io_object_t nextMedia; | |
87 | - kern_return_t kernResult = KERN_FAILURE; | |
88 | - *bsdPath = '\0'; | |
89 | - nextMedia = IOIteratorNext( mediaIterator ); | |
90 | - if ( nextMedia ) | |
91 | - { | |
92 | - CFTypeRef bsdPathAsCFString; | |
93 | - bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); | |
94 | - if ( bsdPathAsCFString ) { | |
95 | - size_t devPathLength; | |
96 | - strcpy( bsdPath, _PATH_DEV ); | |
97 | - strcat( bsdPath, "r" ); | |
98 | - devPathLength = strlen( bsdPath ); | |
99 | - if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { | |
100 | - kernResult = KERN_SUCCESS; | |
101 | - } | |
102 | - CFRelease( bsdPathAsCFString ); | |
103 | - } | |
104 | - IOObjectRelease( nextMedia ); | |
105 | - } | |
106 | - | |
107 | - return kernResult; | |
108 | -} | |
61 | +/* if the FD is not accessed during that time (in ms), we try to | |
62 | + reopen it to see if the disk has been changed */ | |
63 | +#define FD_OPEN_TIMEOUT 1000 | |
109 | 64 | |
65 | +typedef struct BDRVRawState { | |
66 | + int fd; | |
67 | + int type; | |
68 | +#if defined(__linux__) | |
69 | + /* linux floppy specific */ | |
70 | + int fd_open_flags; | |
71 | + int64_t fd_open_time; | |
72 | + int64_t fd_error_time; | |
73 | + int fd_got_error; | |
74 | + int fd_media_changed; | |
110 | 75 | #endif |
76 | +} BDRVRawState; | |
77 | + | |
78 | +static int fd_open(BlockDriverState *bs); | |
111 | 79 | |
112 | 80 | static int raw_open(BlockDriverState *bs, const char *filename, int flags) |
113 | 81 | { |
114 | 82 | BDRVRawState *s = bs->opaque; |
115 | - int fd, open_flags; | |
83 | + int fd, open_flags, ret; | |
116 | 84 | |
117 | -#ifdef CONFIG_COCOA | |
118 | - if (strstart(filename, "/dev/cdrom", NULL)) { | |
119 | - kern_return_t kernResult; | |
120 | - io_iterator_t mediaIterator; | |
121 | - char bsdPath[ MAXPATHLEN ]; | |
122 | - int fd; | |
123 | - | |
124 | - kernResult = FindEjectableCDMedia( &mediaIterator ); | |
125 | - kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); | |
126 | - | |
127 | - if ( bsdPath[ 0 ] != '\0' ) { | |
128 | - strcat(bsdPath,"s0"); | |
129 | - /* some CDs don't have a partition 0 */ | |
130 | - fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); | |
131 | - if (fd < 0) { | |
132 | - bsdPath[strlen(bsdPath)-1] = '1'; | |
133 | - } else { | |
134 | - close(fd); | |
135 | - } | |
136 | - filename = bsdPath; | |
137 | - } | |
138 | - | |
139 | - if ( mediaIterator ) | |
140 | - IOObjectRelease( mediaIterator ); | |
141 | - } | |
142 | -#endif | |
143 | 85 | open_flags = O_BINARY; |
144 | 86 | if ((flags & BDRV_O_ACCESS) == O_RDWR) { |
145 | 87 | open_flags |= O_RDWR; |
... | ... | @@ -150,9 +92,15 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) |
150 | 92 | if (flags & BDRV_O_CREAT) |
151 | 93 | open_flags |= O_CREAT | O_TRUNC; |
152 | 94 | |
95 | + s->type = FTYPE_FILE; | |
96 | + | |
153 | 97 | fd = open(filename, open_flags, 0644); |
154 | - if (fd < 0) | |
155 | - return -errno; | |
98 | + if (fd < 0) { | |
99 | + ret = -errno; | |
100 | + if (ret == -EROFS) | |
101 | + ret = -EACCES; | |
102 | + return ret; | |
103 | + } | |
156 | 104 | s->fd = fd; |
157 | 105 | return 0; |
158 | 106 | } |
... | ... | @@ -180,6 +128,10 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, |
180 | 128 | BDRVRawState *s = bs->opaque; |
181 | 129 | int ret; |
182 | 130 | |
131 | + ret = fd_open(bs); | |
132 | + if (ret < 0) | |
133 | + return ret; | |
134 | + | |
183 | 135 | lseek(s->fd, offset, SEEK_SET); |
184 | 136 | ret = read(s->fd, buf, count); |
185 | 137 | return ret; |
... | ... | @@ -191,13 +143,17 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, |
191 | 143 | BDRVRawState *s = bs->opaque; |
192 | 144 | int ret; |
193 | 145 | |
146 | + ret = fd_open(bs); | |
147 | + if (ret < 0) | |
148 | + return ret; | |
149 | + | |
194 | 150 | lseek(s->fd, offset, SEEK_SET); |
195 | 151 | ret = write(s->fd, buf, count); |
196 | 152 | return ret; |
197 | 153 | } |
198 | 154 | |
199 | 155 | /***********************************************************/ |
200 | -/* Unix AOP using POSIX AIO */ | |
156 | +/* Unix AIO using POSIX AIO */ | |
201 | 157 | |
202 | 158 | typedef struct RawAIOCB { |
203 | 159 | BlockDriverAIOCB common; |
... | ... | @@ -236,15 +192,18 @@ void qemu_aio_init(void) |
236 | 192 | act.sa_handler = aio_signal_handler; |
237 | 193 | sigaction(aio_sig_num, &act, NULL); |
238 | 194 | |
195 | +#if defined(__GLIBC__) && defined(__linux__) | |
239 | 196 | { |
240 | - /* XXX: aio thread exit seems to hang on RH 9 */ | |
197 | + /* XXX: aio thread exit seems to hang on RedHat 9 and this init | |
198 | + seems to fix the problem. */ | |
241 | 199 | struct aioinit ai; |
242 | 200 | memset(&ai, 0, sizeof(ai)); |
243 | - ai.aio_threads = 2; | |
201 | + ai.aio_threads = 1; | |
244 | 202 | ai.aio_num = 1; |
245 | 203 | ai.aio_idle_time = 365 * 100000; |
246 | 204 | aio_init(&ai); |
247 | 205 | } |
206 | +#endif | |
248 | 207 | } |
249 | 208 | |
250 | 209 | void qemu_aio_poll(void) |
... | ... | @@ -270,7 +229,7 @@ void qemu_aio_poll(void) |
270 | 229 | if (ret == acb->aiocb.aio_nbytes) |
271 | 230 | ret = 0; |
272 | 231 | else |
273 | - ret = -1; | |
232 | + ret = -EINVAL; | |
274 | 233 | } else { |
275 | 234 | ret = -ret; |
276 | 235 | } |
... | ... | @@ -329,6 +288,9 @@ static RawAIOCB *raw_aio_setup(BlockDriverState *bs, |
329 | 288 | BDRVRawState *s = bs->opaque; |
330 | 289 | RawAIOCB *acb; |
331 | 290 | |
291 | + if (fd_open(bs) < 0) | |
292 | + return NULL; | |
293 | + | |
332 | 294 | acb = qemu_aio_get(bs, cb, opaque); |
333 | 295 | if (!acb) |
334 | 296 | return NULL; |
... | ... | @@ -405,12 +367,17 @@ static void raw_aio_cancel(BlockDriverAIOCB *blockacb) |
405 | 367 | static void raw_close(BlockDriverState *bs) |
406 | 368 | { |
407 | 369 | BDRVRawState *s = bs->opaque; |
408 | - close(s->fd); | |
370 | + if (s->fd >= 0) { | |
371 | + close(s->fd); | |
372 | + s->fd = -1; | |
373 | + } | |
409 | 374 | } |
410 | 375 | |
411 | 376 | static int raw_truncate(BlockDriverState *bs, int64_t offset) |
412 | 377 | { |
413 | 378 | BDRVRawState *s = bs->opaque; |
379 | + if (s->type != FTYPE_FILE) | |
380 | + return -ENOTSUP; | |
414 | 381 | if (ftruncate(s->fd, offset) < 0) |
415 | 382 | return -errno; |
416 | 383 | return 0; |
... | ... | @@ -428,6 +395,11 @@ static int64_t raw_getlength(BlockDriverState *bs) |
428 | 395 | struct dk_minfo minfo; |
429 | 396 | int rv; |
430 | 397 | #endif |
398 | + int ret; | |
399 | + | |
400 | + ret = fd_open(bs); | |
401 | + if (ret < 0) | |
402 | + return ret; | |
431 | 403 | |
432 | 404 | #ifdef _BSD |
433 | 405 | if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { |
... | ... | @@ -455,12 +427,6 @@ static int64_t raw_getlength(BlockDriverState *bs) |
455 | 427 | { |
456 | 428 | size = lseek(fd, 0, SEEK_END); |
457 | 429 | } |
458 | -#ifdef _WIN32 | |
459 | - /* On Windows hosts it can happen that we're unable to get file size | |
460 | - for CD-ROM raw device (it's inherent limitation of the CDFS driver). */ | |
461 | - if (size == -1) | |
462 | - size = LONG_LONG_MAX; | |
463 | -#endif | |
464 | 430 | return size; |
465 | 431 | } |
466 | 432 | |
... | ... | @@ -509,13 +475,358 @@ BlockDriver bdrv_raw = { |
509 | 475 | .bdrv_getlength = raw_getlength, |
510 | 476 | }; |
511 | 477 | |
478 | +/***********************************************/ | |
479 | +/* host device */ | |
480 | + | |
481 | +#ifdef CONFIG_COCOA | |
482 | +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); | |
483 | +static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); | |
484 | + | |
485 | +kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) | |
486 | +{ | |
487 | + kern_return_t kernResult; | |
488 | + mach_port_t masterPort; | |
489 | + CFMutableDictionaryRef classesToMatch; | |
490 | + | |
491 | + kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); | |
492 | + if ( KERN_SUCCESS != kernResult ) { | |
493 | + printf( "IOMasterPort returned %d\n", kernResult ); | |
494 | + } | |
495 | + | |
496 | + classesToMatch = IOServiceMatching( kIOCDMediaClass ); | |
497 | + if ( classesToMatch == NULL ) { | |
498 | + printf( "IOServiceMatching returned a NULL dictionary.\n" ); | |
499 | + } else { | |
500 | + CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); | |
501 | + } | |
502 | + kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); | |
503 | + if ( KERN_SUCCESS != kernResult ) | |
504 | + { | |
505 | + printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); | |
506 | + } | |
507 | + | |
508 | + return kernResult; | |
509 | +} | |
510 | + | |
511 | +kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) | |
512 | +{ | |
513 | + io_object_t nextMedia; | |
514 | + kern_return_t kernResult = KERN_FAILURE; | |
515 | + *bsdPath = '\0'; | |
516 | + nextMedia = IOIteratorNext( mediaIterator ); | |
517 | + if ( nextMedia ) | |
518 | + { | |
519 | + CFTypeRef bsdPathAsCFString; | |
520 | + bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); | |
521 | + if ( bsdPathAsCFString ) { | |
522 | + size_t devPathLength; | |
523 | + strcpy( bsdPath, _PATH_DEV ); | |
524 | + strcat( bsdPath, "r" ); | |
525 | + devPathLength = strlen( bsdPath ); | |
526 | + if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { | |
527 | + kernResult = KERN_SUCCESS; | |
528 | + } | |
529 | + CFRelease( bsdPathAsCFString ); | |
530 | + } | |
531 | + IOObjectRelease( nextMedia ); | |
532 | + } | |
533 | + | |
534 | + return kernResult; | |
535 | +} | |
536 | + | |
537 | +#endif | |
538 | + | |
539 | +static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | |
540 | +{ | |
541 | + BDRVRawState *s = bs->opaque; | |
542 | + int fd, open_flags, ret; | |
543 | + | |
544 | +#ifdef CONFIG_COCOA | |
545 | + if (strstart(filename, "/dev/cdrom", NULL)) { | |
546 | + kern_return_t kernResult; | |
547 | + io_iterator_t mediaIterator; | |
548 | + char bsdPath[ MAXPATHLEN ]; | |
549 | + int fd; | |
550 | + | |
551 | + kernResult = FindEjectableCDMedia( &mediaIterator ); | |
552 | + kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); | |
553 | + | |
554 | + if ( bsdPath[ 0 ] != '\0' ) { | |
555 | + strcat(bsdPath,"s0"); | |
556 | + /* some CDs don't have a partition 0 */ | |
557 | + fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); | |
558 | + if (fd < 0) { | |
559 | + bsdPath[strlen(bsdPath)-1] = '1'; | |
560 | + } else { | |
561 | + close(fd); | |
562 | + } | |
563 | + filename = bsdPath; | |
564 | + } | |
565 | + | |
566 | + if ( mediaIterator ) | |
567 | + IOObjectRelease( mediaIterator ); | |
568 | + } | |
569 | +#endif | |
570 | + open_flags = O_BINARY; | |
571 | + if ((flags & BDRV_O_ACCESS) == O_RDWR) { | |
572 | + open_flags |= O_RDWR; | |
573 | + } else { | |
574 | + open_flags |= O_RDONLY; | |
575 | + bs->read_only = 1; | |
576 | + } | |
577 | + | |
578 | + s->type = FTYPE_FILE; | |
579 | +#if defined(__linux__) | |
580 | + if (strstart(filename, "/dev/cd", NULL)) { | |
581 | + /* open will not fail even if no CD is inserted */ | |
582 | + open_flags |= O_NONBLOCK; | |
583 | + s->type = FTYPE_CD; | |
584 | + } else if (strstart(filename, "/dev/fd", NULL)) { | |
585 | + s->type = FTYPE_FD; | |
586 | + s->fd_open_flags = open_flags; | |
587 | + /* open will not fail even if no floppy is inserted */ | |
588 | + open_flags |= O_NONBLOCK; | |
589 | + } | |
590 | +#endif | |
591 | + fd = open(filename, open_flags, 0644); | |
592 | + if (fd < 0) { | |
593 | + ret = -errno; | |
594 | + if (ret == -EROFS) | |
595 | + ret = -EACCES; | |
596 | + return ret; | |
597 | + } | |
598 | + s->fd = fd; | |
599 | +#if defined(__linux__) | |
600 | + /* close fd so that we can reopen it as needed */ | |
601 | + if (s->type == FTYPE_FD) { | |
602 | + close(s->fd); | |
603 | + s->fd = -1; | |
604 | + s->fd_media_changed = 1; | |
605 | + } | |
606 | +#endif | |
607 | + return 0; | |
608 | +} | |
609 | + | |
610 | +#if defined(__linux__) && !defined(QEMU_TOOL) | |
611 | + | |
612 | +/* Note: we do not have a reliable method to detect if the floppy is | |
613 | + present. The current method is to try to open the floppy at every | |
614 | + I/O and to keep it opened during a few hundreds of ms. */ | |
615 | +static int fd_open(BlockDriverState *bs) | |
616 | +{ | |
617 | + BDRVRawState *s = bs->opaque; | |
618 | + int last_media_present; | |
619 | + | |
620 | + if (s->type != FTYPE_FD) | |
621 | + return 0; | |
622 | + last_media_present = (s->fd >= 0); | |
623 | + if (s->fd >= 0 && | |
624 | + (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { | |
625 | + close(s->fd); | |
626 | + s->fd = -1; | |
627 | +#ifdef DEBUG_FLOPPY | |
628 | + printf("Floppy closed\n"); | |
629 | +#endif | |
630 | + } | |
631 | + if (s->fd < 0) { | |
632 | + if (s->fd_got_error && | |
633 | + (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) { | |
634 | +#ifdef DEBUG_FLOPPY | |
635 | + printf("No floppy (open delayed)\n"); | |
636 | +#endif | |
637 | + return -EIO; | |
638 | + } | |
639 | + s->fd = open(bs->filename, s->fd_open_flags); | |
640 | + if (s->fd < 0) { | |
641 | + s->fd_error_time = qemu_get_clock(rt_clock); | |
642 | + s->fd_got_error = 1; | |
643 | + if (last_media_present) | |
644 | + s->fd_media_changed = 1; | |
645 | +#ifdef DEBUG_FLOPPY | |
646 | + printf("No floppy\n"); | |
647 | +#endif | |
648 | + return -EIO; | |
649 | + } | |
650 | +#ifdef DEBUG_FLOPPY | |
651 | + printf("Floppy opened\n"); | |
652 | +#endif | |
653 | + } | |
654 | + if (!last_media_present) | |
655 | + s->fd_media_changed = 1; | |
656 | + s->fd_open_time = qemu_get_clock(rt_clock); | |
657 | + s->fd_got_error = 0; | |
658 | + return 0; | |
659 | +} | |
660 | +#else | |
661 | +static int fd_open(BlockDriverState *bs) | |
662 | +{ | |
663 | + return 0; | |
664 | +} | |
665 | +#endif | |
666 | + | |
667 | +#if defined(__linux__) | |
668 | + | |
669 | +static int raw_is_inserted(BlockDriverState *bs) | |
670 | +{ | |
671 | + BDRVRawState *s = bs->opaque; | |
672 | + int ret; | |
673 | + | |
674 | + switch(s->type) { | |
675 | + case FTYPE_CD: | |
676 | + ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); | |
677 | + if (ret == CDS_DISC_OK) | |
678 | + return 1; | |
679 | + else | |
680 | + return 0; | |
681 | + break; | |
682 | + case FTYPE_FD: | |
683 | + ret = fd_open(bs); | |
684 | + return (ret >= 0); | |
685 | + default: | |
686 | + return 1; | |
687 | + } | |
688 | +} | |
689 | + | |
690 | +/* currently only used by fdc.c, but a CD version would be good too */ | |
691 | +static int raw_media_changed(BlockDriverState *bs) | |
692 | +{ | |
693 | + BDRVRawState *s = bs->opaque; | |
694 | + | |
695 | + switch(s->type) { | |
696 | + case FTYPE_FD: | |
697 | + { | |
698 | + int ret; | |
699 | + /* XXX: we do not have a true media changed indication. It | |
700 | + does not work if the floppy is changed without trying | |
701 | + to read it */ | |
702 | + fd_open(bs); | |
703 | + ret = s->fd_media_changed; | |
704 | + s->fd_media_changed = 0; | |
705 | +#ifdef DEBUG_FLOPPY | |
706 | + printf("Floppy changed=%d\n", ret); | |
707 | +#endif | |
708 | + return ret; | |
709 | + } | |
710 | + default: | |
711 | + return -ENOTSUP; | |
712 | + } | |
713 | +} | |
714 | + | |
715 | +static int raw_eject(BlockDriverState *bs, int eject_flag) | |
716 | +{ | |
717 | + BDRVRawState *s = bs->opaque; | |
718 | + | |
719 | + switch(s->type) { | |
720 | + case FTYPE_CD: | |
721 | + if (eject_flag) { | |
722 | + if (ioctl (s->fd, CDROMEJECT, NULL) < 0) | |
723 | + perror("CDROMEJECT"); | |
724 | + } else { | |
725 | + if (ioctl (s->fd, CDROMCLOSETRAY, NULL) < 0) | |
726 | + perror("CDROMEJECT"); | |
727 | + } | |
728 | + break; | |
729 | + case FTYPE_FD: | |
730 | + { | |
731 | + int fd; | |
732 | + if (s->fd >= 0) { | |
733 | + close(s->fd); | |
734 | + s->fd = -1; | |
735 | + } | |
736 | + fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK); | |
737 | + if (fd >= 0) { | |
738 | + if (ioctl(fd, FDEJECT, 0) < 0) | |
739 | + perror("FDEJECT"); | |
740 | + close(fd); | |
741 | + } | |
742 | + } | |
743 | + break; | |
744 | + default: | |
745 | + return -ENOTSUP; | |
746 | + } | |
747 | + return 0; | |
748 | +} | |
749 | + | |
750 | +static int raw_set_locked(BlockDriverState *bs, int locked) | |
751 | +{ | |
752 | + BDRVRawState *s = bs->opaque; | |
753 | + | |
754 | + switch(s->type) { | |
755 | + case FTYPE_CD: | |
756 | + if (ioctl (s->fd, CDROM_LOCKDOOR, locked) < 0) { | |
757 | + /* Note: an error can happen if the distribution automatically | |
758 | + mounts the CD-ROM */ | |
759 | + // perror("CDROM_LOCKDOOR"); | |
760 | + } | |
761 | + break; | |
762 | + default: | |
763 | + return -ENOTSUP; | |
764 | + } | |
765 | + return 0; | |
766 | +} | |
767 | + | |
768 | +#else | |
769 | + | |
770 | +static int raw_is_inserted(BlockDriverState *bs) | |
771 | +{ | |
772 | + return 1; | |
773 | +} | |
774 | + | |
775 | +static int raw_media_changed(BlockDriverState *bs) | |
776 | +{ | |
777 | + return -ENOTSUP; | |
778 | +} | |
779 | + | |
780 | +static int raw_eject(BlockDriverState *bs, int eject_flag) | |
781 | +{ | |
782 | + return -ENOTSUP; | |
783 | +} | |
784 | + | |
785 | +static int raw_set_locked(BlockDriverState *bs, int locked) | |
786 | +{ | |
787 | + return -ENOTSUP; | |
788 | +} | |
789 | + | |
790 | +#endif /* !linux */ | |
791 | + | |
792 | +BlockDriver bdrv_host_device = { | |
793 | + "host_device", | |
794 | + sizeof(BDRVRawState), | |
795 | + NULL, /* no probe for protocols */ | |
796 | + hdev_open, | |
797 | + NULL, | |
798 | + NULL, | |
799 | + raw_close, | |
800 | + NULL, | |
801 | + raw_flush, | |
802 | + | |
803 | + .bdrv_aio_read = raw_aio_read, | |
804 | + .bdrv_aio_write = raw_aio_write, | |
805 | + .bdrv_aio_cancel = raw_aio_cancel, | |
806 | + .aiocb_size = sizeof(RawAIOCB), | |
807 | + .bdrv_pread = raw_pread, | |
808 | + .bdrv_pwrite = raw_pwrite, | |
809 | + .bdrv_getlength = raw_getlength, | |
810 | + | |
811 | + /* removable device support */ | |
812 | + .bdrv_is_inserted = raw_is_inserted, | |
813 | + .bdrv_media_changed = raw_media_changed, | |
814 | + .bdrv_eject = raw_eject, | |
815 | + .bdrv_set_locked = raw_set_locked, | |
816 | +}; | |
817 | + | |
512 | 818 | #else /* _WIN32 */ |
513 | 819 | |
514 | 820 | /* XXX: use another file ? */ |
515 | 821 | #include <winioctl.h> |
516 | 822 | |
823 | +#define FTYPE_FILE 0 | |
824 | +#define FTYPE_CD 1 | |
825 | + | |
517 | 826 | typedef struct BDRVRawState { |
518 | 827 | HANDLE hfile; |
828 | + int type; | |
829 | + char drive_letter[2]; | |
519 | 830 | } BDRVRawState; |
520 | 831 | |
521 | 832 | typedef struct RawAIOCB { |
... | ... | @@ -565,6 +876,23 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) |
565 | 876 | BDRVRawState *s = bs->opaque; |
566 | 877 | int access_flags, create_flags; |
567 | 878 | DWORD overlapped; |
879 | + char device_name[64]; | |
880 | + const char *p; | |
881 | + | |
882 | + if (strstart(filename, "/dev/cdrom", NULL)) { | |
883 | + if (find_cdrom(device_name, sizeof(device_name)) < 0) | |
884 | + return -ENOENT; | |
885 | + filename = device_name; | |
886 | + } else { | |
887 | + /* transform drive letters into device name */ | |
888 | + if (((filename[0] >= 'a' && filename[0] <= 'z') || | |
889 | + (filename[0] >= 'A' && filename[0] <= 'Z')) && | |
890 | + filename[1] == ':' && filename[2] == '\0') { | |
891 | + snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]); | |
892 | + filename = device_name; | |
893 | + } | |
894 | + } | |
895 | + s->type = find_device_type(filename); | |
568 | 896 | |
569 | 897 | if ((flags & BDRV_O_ACCESS) == O_RDWR) { |
570 | 898 | access_flags = GENERIC_READ | GENERIC_WRITE; |
... | ... | @@ -765,10 +1093,22 @@ static int64_t raw_getlength(BlockDriverState *bs) |
765 | 1093 | { |
766 | 1094 | BDRVRawState *s = bs->opaque; |
767 | 1095 | LARGE_INTEGER l; |
1096 | + ULARGE_INTEGER available, total, total_free; | |
768 | 1097 | |
769 | - l.LowPart = GetFileSize(s->hfile, &l.HighPart); | |
770 | - if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) | |
771 | - return -EIO; | |
1098 | + switch(s->ftype) { | |
1099 | + case FTYPE_FILE: | |
1100 | + l.LowPart = GetFileSize(s->hfile, &l.HighPart); | |
1101 | + if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) | |
1102 | + return -EIO; | |
1103 | + break; | |
1104 | + case FTYPE_CD: | |
1105 | + if (!GetDiskFreeSpaceEx(s->drive_letter, &available, &total, &total_free)) | |
1106 | + return -EIO; | |
1107 | + l = total; | |
1108 | + break; | |
1109 | + default: | |
1110 | + return -EIO; | |
1111 | + } | |
772 | 1112 | return l.QuadPart; |
773 | 1113 | } |
774 | 1114 | |
... | ... | @@ -833,4 +1173,146 @@ BlockDriver bdrv_raw = { |
833 | 1173 | .bdrv_truncate = raw_truncate, |
834 | 1174 | .bdrv_getlength = raw_getlength, |
835 | 1175 | }; |
1176 | + | |
1177 | +/***********************************************/ | |
1178 | +/* host device */ | |
1179 | + | |
1180 | +static int find_cdrom(char *cdrom_name, int cdrom_name_size) | |
1181 | +{ | |
1182 | + char drives[256], *pdrv = drives; | |
1183 | + UINT type; | |
1184 | + | |
1185 | + memset(drives, 0, sizeof(drivers)); | |
1186 | + GetLogicalDriveStrings(sizeof(drives), drives); | |
1187 | + while(pdrv[0] != '\0') { | |
1188 | + type = GetDriveType(pdrv); | |
1189 | + switch(type) { | |
1190 | + case DRIVE_CDROM: | |
1191 | + snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]); | |
1192 | + return 0; | |
1193 | + break; | |
1194 | + } | |
1195 | + pdrv += lstrlen(pdrv) + 1; | |
1196 | + } | |
1197 | + return -1; | |
1198 | +} | |
1199 | + | |
1200 | +static int find_device_type(const char *filename) | |
1201 | +{ | |
1202 | + UINT type; | |
1203 | + const char *p; | |
1204 | + | |
1205 | + if (strstart(filename, "\\\\.\\", &p) || | |
1206 | + strstart(filename, "//./", &p)) { | |
1207 | + s->drive_letter[0] = p[0]; | |
1208 | + s->drive_letter[1] = '\0'; | |
1209 | + type = GetDriveType(s->drive_letter); | |
1210 | + if (type == DRIVE_CDROM) | |
1211 | + return FTYPE_CD; | |
1212 | + else | |
1213 | + return FTYPE_FILE; | |
1214 | + } else { | |
1215 | + return FTYPE_FILE; | |
1216 | + } | |
1217 | +} | |
1218 | + | |
1219 | +static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | |
1220 | +{ | |
1221 | + BDRVRawState *s = bs->opaque; | |
1222 | + int access_flags, create_flags; | |
1223 | + DWORD overlapped; | |
1224 | + char device_name[64]; | |
1225 | + const char *p; | |
1226 | + | |
1227 | + if (strstart(filename, "/dev/cdrom", NULL)) { | |
1228 | + if (find_cdrom(device_name, sizeof(device_name)) < 0) | |
1229 | + return -ENOENT; | |
1230 | + filename = device_name; | |
1231 | + } else { | |
1232 | + /* transform drive letters into device name */ | |
1233 | + if (((filename[0] >= 'a' && filename[0] <= 'z') || | |
1234 | + (filename[0] >= 'A' && filename[0] <= 'Z')) && | |
1235 | + filename[1] == ':' && filename[2] == '\0') { | |
1236 | + snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]); | |
1237 | + filename = device_name; | |
1238 | + } | |
1239 | + } | |
1240 | + s->type = find_device_type(filename); | |
1241 | + | |
1242 | + if ((flags & BDRV_O_ACCESS) == O_RDWR) { | |
1243 | + access_flags = GENERIC_READ | GENERIC_WRITE; | |
1244 | + } else { | |
1245 | + access_flags = GENERIC_READ; | |
1246 | + } | |
1247 | + create_flags = OPEN_EXISTING; | |
1248 | + | |
1249 | +#ifdef QEMU_TOOL | |
1250 | + overlapped = 0; | |
1251 | +#else | |
1252 | + overlapped = FILE_FLAG_OVERLAPPED; | |
1253 | +#endif | |
1254 | + s->hfile = CreateFile(filename, access_flags, | |
1255 | + FILE_SHARE_READ, NULL, | |
1256 | + create_flags, overlapped, 0); | |
1257 | + if (s->hfile == INVALID_HANDLE_VALUE) | |
1258 | + return -1; | |
1259 | + return 0; | |
1260 | +} | |
1261 | + | |
1262 | +#if 0 | |
1263 | +/***********************************************/ | |
1264 | +/* removable device additionnal commands */ | |
1265 | + | |
1266 | +static int raw_is_inserted(BlockDriverState *bs) | |
1267 | +{ | |
1268 | + return 1; | |
1269 | +} | |
1270 | + | |
1271 | +static int raw_media_changed(BlockDriverState *bs) | |
1272 | +{ | |
1273 | + return -ENOTSUP; | |
1274 | +} | |
1275 | + | |
1276 | +static int raw_eject(BlockDriverState *bs, int eject_flag) | |
1277 | +{ | |
1278 | + DWORD ret_count; | |
1279 | + | |
1280 | + if (s->type == FTYPE_FILE) | |
1281 | + return -ENOTSUP; | |
1282 | + if (eject_flag) { | |
1283 | + DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA, | |
1284 | + NULL, 0, NULL, 0, &lpBytesReturned, NULL); | |
1285 | + } else { | |
1286 | + DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA, | |
1287 | + NULL, 0, NULL, 0, &lpBytesReturned, NULL); | |
1288 | + } | |
1289 | +} | |
1290 | + | |
1291 | +static int raw_set_locked(BlockDriverState *bs, int locked) | |
1292 | +{ | |
1293 | + return -ENOTSUP; | |
1294 | +} | |
1295 | +#endif | |
1296 | + | |
1297 | +BlockDriver bdrv_host_device = { | |
1298 | + "host_device", | |
1299 | + sizeof(BDRVRawState), | |
1300 | + NULL, /* no probe for protocols */ | |
1301 | + hdev_open, | |
1302 | + NULL, | |
1303 | + NULL, | |
1304 | + raw_close, | |
1305 | + NULL, | |
1306 | + raw_flush, | |
1307 | + | |
1308 | +#if 0 | |
1309 | + .bdrv_aio_read = raw_aio_read, | |
1310 | + .bdrv_aio_write = raw_aio_write, | |
1311 | + .bdrv_aio_cancel = raw_aio_cancel, | |
1312 | + .aiocb_size = sizeof(RawAIOCB); | |
1313 | +#endif | |
1314 | + .bdrv_pread = raw_pread, | |
1315 | + .bdrv_pwrite = raw_pwrite, | |
1316 | + .bdrv_getlength = raw_getlength, | |
1317 | +}; | |
836 | 1318 | #endif /* _WIN32 */ | ... | ... |
block.c
... | ... | @@ -181,24 +181,37 @@ void get_tmp_filename(char *filename, int size) |
181 | 181 | } |
182 | 182 | #endif |
183 | 183 | |
184 | +#ifdef _WIN32 | |
185 | +static int is_windows_drive(const char *filename) | |
186 | +{ | |
187 | + if (((filename[0] >= 'a' && filename[0] <= 'z') || | |
188 | + (filename[0] >= 'A' && filename[0] <= 'Z')) && | |
189 | + filename[1] == ':' && filename[2] == '\0') | |
190 | + return 1; | |
191 | + if (strstart(filename, "\\\\.\\", NULL) || | |
192 | + strstart(filename, "//./", NULL)) | |
193 | + return 1; | |
194 | + return 0; | |
195 | +} | |
196 | +#endif | |
197 | + | |
184 | 198 | static BlockDriver *find_protocol(const char *filename) |
185 | 199 | { |
186 | 200 | BlockDriver *drv1; |
187 | 201 | char protocol[128]; |
188 | 202 | int len; |
189 | 203 | const char *p; |
204 | + | |
205 | +#ifdef _WIN32 | |
206 | + if (is_windows_drive(filename)) | |
207 | + return &bdrv_raw; | |
208 | +#endif | |
190 | 209 | p = strchr(filename, ':'); |
191 | 210 | if (!p) |
192 | 211 | return &bdrv_raw; |
193 | 212 | len = p - filename; |
194 | 213 | if (len > sizeof(protocol) - 1) |
195 | 214 | len = sizeof(protocol) - 1; |
196 | -#ifdef _WIN32 | |
197 | - if (len == 1) { | |
198 | - /* specific win32 case for driver letters */ | |
199 | - return &bdrv_raw; | |
200 | - } | |
201 | -#endif | |
202 | 215 | memcpy(protocol, filename, len); |
203 | 216 | protocol[len] = '\0'; |
204 | 217 | for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { |
... | ... | @@ -218,14 +231,28 @@ static BlockDriver *find_image_format(const char *filename) |
218 | 231 | uint8_t buf[2048]; |
219 | 232 | BlockDriverState *bs; |
220 | 233 | |
234 | + /* detect host devices. By convention, /dev/cdrom[N] is always | |
235 | + recognized as a host CDROM */ | |
236 | + if (strstart(filename, "/dev/cdrom", NULL)) | |
237 | + return &bdrv_host_device; | |
238 | +#ifdef _WIN32 | |
239 | + if (is_windows_drive(filename)) | |
240 | + return &bdrv_host_device; | |
241 | +#else | |
242 | + { | |
243 | + struct stat st; | |
244 | + if (stat(filename, &st) >= 0 && | |
245 | + (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { | |
246 | + return &bdrv_host_device; | |
247 | + } | |
248 | + } | |
249 | +#endif | |
250 | + | |
221 | 251 | drv = find_protocol(filename); |
222 | - /* no need to test disk image formats for vvfat or host specific | |
223 | - devices */ | |
252 | + /* no need to test disk image formats for vvfat */ | |
224 | 253 | if (drv == &bdrv_vvfat) |
225 | 254 | return drv; |
226 | - if (strstart(filename, "/dev/", NULL)) | |
227 | - return &bdrv_raw; | |
228 | - | |
255 | + | |
229 | 256 | ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY); |
230 | 257 | if (ret < 0) |
231 | 258 | return NULL; |
... | ... | @@ -362,9 +389,8 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, |
362 | 389 | goto fail; |
363 | 390 | } |
364 | 391 | |
365 | - bs->inserted = 1; | |
366 | - | |
367 | 392 | /* call the change callback */ |
393 | + bs->media_changed = 1; | |
368 | 394 | if (bs->change_cb) |
369 | 395 | bs->change_cb(bs->change_opaque); |
370 | 396 | |
... | ... | @@ -373,7 +399,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, |
373 | 399 | |
374 | 400 | void bdrv_close(BlockDriverState *bs) |
375 | 401 | { |
376 | - if (bs->inserted) { | |
402 | + if (bs->drv) { | |
377 | 403 | if (bs->backing_hd) |
378 | 404 | bdrv_delete(bs->backing_hd); |
379 | 405 | bs->drv->bdrv_close(bs); |
... | ... | @@ -385,9 +411,9 @@ void bdrv_close(BlockDriverState *bs) |
385 | 411 | #endif |
386 | 412 | bs->opaque = NULL; |
387 | 413 | bs->drv = NULL; |
388 | - bs->inserted = 0; | |
389 | 414 | |
390 | 415 | /* call the change callback */ |
416 | + bs->media_changed = 1; | |
391 | 417 | if (bs->change_cb) |
392 | 418 | bs->change_cb(bs->change_opaque); |
393 | 419 | } |
... | ... | @@ -403,12 +429,13 @@ void bdrv_delete(BlockDriverState *bs) |
403 | 429 | /* commit COW file into the raw image */ |
404 | 430 | int bdrv_commit(BlockDriverState *bs) |
405 | 431 | { |
432 | + BlockDriver *drv = bs->drv; | |
406 | 433 | int64_t i, total_sectors; |
407 | 434 | int n, j; |
408 | 435 | unsigned char sector[512]; |
409 | 436 | |
410 | - if (!bs->inserted) | |
411 | - return -ENOENT; | |
437 | + if (!drv) | |
438 | + return -ENOMEDIUM; | |
412 | 439 | |
413 | 440 | if (bs->read_only) { |
414 | 441 | return -EACCES; |
... | ... | @@ -420,7 +447,7 @@ int bdrv_commit(BlockDriverState *bs) |
420 | 447 | |
421 | 448 | total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; |
422 | 449 | for (i = 0; i < total_sectors;) { |
423 | - if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) { | |
450 | + if (drv->bdrv_is_allocated(bs, i, 65536, &n)) { | |
424 | 451 | for(j = 0; j < n; j++) { |
425 | 452 | if (bdrv_read(bs, i, sector, 1) != 0) { |
426 | 453 | return -EIO; |
... | ... | @@ -436,20 +463,20 @@ int bdrv_commit(BlockDriverState *bs) |
436 | 463 | } |
437 | 464 | } |
438 | 465 | |
439 | - if (bs->drv->bdrv_make_empty) | |
440 | - return bs->drv->bdrv_make_empty(bs); | |
466 | + if (drv->bdrv_make_empty) | |
467 | + return drv->bdrv_make_empty(bs); | |
441 | 468 | |
442 | 469 | return 0; |
443 | 470 | } |
444 | 471 | |
445 | -/* return < 0 if error */ | |
472 | +/* return < 0 if error. See bdrv_write() for the return codes */ | |
446 | 473 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
447 | 474 | uint8_t *buf, int nb_sectors) |
448 | 475 | { |
449 | 476 | BlockDriver *drv = bs->drv; |
450 | 477 | |
451 | - if (!bs->inserted) | |
452 | - return -1; | |
478 | + if (!drv) | |
479 | + return -ENOMEDIUM; | |
453 | 480 | |
454 | 481 | if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { |
455 | 482 | memcpy(buf, bs->boot_sector_data, 512); |
... | ... | @@ -466,7 +493,7 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
466 | 493 | if (ret < 0) |
467 | 494 | return ret; |
468 | 495 | else if (ret != len) |
469 | - return -EIO; | |
496 | + return -EINVAL; | |
470 | 497 | else |
471 | 498 | return 0; |
472 | 499 | } else { |
... | ... | @@ -474,15 +501,20 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
474 | 501 | } |
475 | 502 | } |
476 | 503 | |
477 | -/* return < 0 if error */ | |
504 | +/* Return < 0 if error. Important errors are: | |
505 | + -EIO generic I/O error (may happen for all errors) | |
506 | + -ENOMEDIUM No media inserted. | |
507 | + -EINVAL Invalid sector number or nb_sectors | |
508 | + -EACCES Trying to write a read-only device | |
509 | +*/ | |
478 | 510 | int bdrv_write(BlockDriverState *bs, int64_t sector_num, |
479 | 511 | const uint8_t *buf, int nb_sectors) |
480 | 512 | { |
481 | 513 | BlockDriver *drv = bs->drv; |
482 | - if (!bs->inserted) | |
483 | - return -1; | |
514 | + if (!bs->drv) | |
515 | + return -ENOMEDIUM; | |
484 | 516 | if (bs->read_only) |
485 | - return -1; | |
517 | + return -EACCES; | |
486 | 518 | if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { |
487 | 519 | memcpy(bs->boot_sector_data, buf, 512); |
488 | 520 | } |
... | ... | @@ -501,7 +533,6 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, |
501 | 533 | } |
502 | 534 | } |
503 | 535 | |
504 | -/* not necessary now */ | |
505 | 536 | static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, |
506 | 537 | uint8_t *buf, int count1) |
507 | 538 | { |
... | ... | @@ -603,7 +634,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, |
603 | 634 | BlockDriver *drv = bs->drv; |
604 | 635 | |
605 | 636 | if (!drv) |
606 | - return -ENOENT; | |
637 | + return -ENOMEDIUM; | |
607 | 638 | if (!drv->bdrv_pread) |
608 | 639 | return bdrv_pread_em(bs, offset, buf1, count1); |
609 | 640 | return drv->bdrv_pread(bs, offset, buf1, count1); |
... | ... | @@ -618,7 +649,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, |
618 | 649 | BlockDriver *drv = bs->drv; |
619 | 650 | |
620 | 651 | if (!drv) |
621 | - return -ENOENT; | |
652 | + return -ENOMEDIUM; | |
622 | 653 | if (!drv->bdrv_pwrite) |
623 | 654 | return bdrv_pwrite_em(bs, offset, buf1, count1); |
624 | 655 | return drv->bdrv_pwrite(bs, offset, buf1, count1); |
... | ... | @@ -631,7 +662,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset) |
631 | 662 | { |
632 | 663 | BlockDriver *drv = bs->drv; |
633 | 664 | if (!drv) |
634 | - return -ENOENT; | |
665 | + return -ENOMEDIUM; | |
635 | 666 | if (!drv->bdrv_truncate) |
636 | 667 | return -ENOTSUP; |
637 | 668 | return drv->bdrv_truncate(bs, offset); |
... | ... | @@ -644,7 +675,7 @@ int64_t bdrv_getlength(BlockDriverState *bs) |
644 | 675 | { |
645 | 676 | BlockDriver *drv = bs->drv; |
646 | 677 | if (!drv) |
647 | - return -ENOENT; | |
678 | + return -ENOMEDIUM; | |
648 | 679 | if (!drv->bdrv_getlength) { |
649 | 680 | /* legacy mode */ |
650 | 681 | return bs->total_sectors * SECTOR_SIZE; |
... | ... | @@ -652,9 +683,16 @@ int64_t bdrv_getlength(BlockDriverState *bs) |
652 | 683 | return drv->bdrv_getlength(bs); |
653 | 684 | } |
654 | 685 | |
686 | +/* return 0 as number of sectors if no device present or error */ | |
655 | 687 | void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) |
656 | 688 | { |
657 | - *nb_sectors_ptr = bs->total_sectors; | |
689 | + int64_t length; | |
690 | + length = bdrv_getlength(bs); | |
691 | + if (length < 0) | |
692 | + length = 0; | |
693 | + else | |
694 | + length = length >> SECTOR_BITS; | |
695 | + *nb_sectors_ptr = length; | |
658 | 696 | } |
659 | 697 | |
660 | 698 | /* force a given boot sector. */ |
... | ... | @@ -715,21 +753,7 @@ int bdrv_is_read_only(BlockDriverState *bs) |
715 | 753 | return bs->read_only; |
716 | 754 | } |
717 | 755 | |
718 | -int bdrv_is_inserted(BlockDriverState *bs) | |
719 | -{ | |
720 | - return bs->inserted; | |
721 | -} | |
722 | - | |
723 | -int bdrv_is_locked(BlockDriverState *bs) | |
724 | -{ | |
725 | - return bs->locked; | |
726 | -} | |
727 | - | |
728 | -void bdrv_set_locked(BlockDriverState *bs, int locked) | |
729 | -{ | |
730 | - bs->locked = locked; | |
731 | -} | |
732 | - | |
756 | +/* XXX: no longer used */ | |
733 | 757 | void bdrv_set_change_cb(BlockDriverState *bs, |
734 | 758 | void (*change_cb)(void *opaque), void *opaque) |
735 | 759 | { |
... | ... | @@ -761,7 +785,7 @@ int bdrv_set_key(BlockDriverState *bs, const char *key) |
761 | 785 | |
762 | 786 | void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) |
763 | 787 | { |
764 | - if (!bs->inserted || !bs->drv) { | |
788 | + if (!bs->drv) { | |
765 | 789 | buf[0] = '\0'; |
766 | 790 | } else { |
767 | 791 | pstrcpy(buf, buf_size, bs->drv->format_name); |
... | ... | @@ -833,7 +857,7 @@ void bdrv_info(void) |
833 | 857 | if (bs->removable) { |
834 | 858 | term_printf(" locked=%d", bs->locked); |
835 | 859 | } |
836 | - if (bs->inserted) { | |
860 | + if (bs->drv) { | |
837 | 861 | term_printf(" file=%s", bs->filename); |
838 | 862 | if (bs->backing_file[0] != '\0') |
839 | 863 | term_printf(" backing_file=%s", bs->backing_file); |
... | ... | @@ -863,7 +887,7 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, |
863 | 887 | { |
864 | 888 | BlockDriver *drv = bs->drv; |
865 | 889 | if (!drv) |
866 | - return -ENOENT; | |
890 | + return -ENOMEDIUM; | |
867 | 891 | if (!drv->bdrv_write_compressed) |
868 | 892 | return -ENOTSUP; |
869 | 893 | return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); |
... | ... | @@ -873,7 +897,7 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) |
873 | 897 | { |
874 | 898 | BlockDriver *drv = bs->drv; |
875 | 899 | if (!drv) |
876 | - return -ENOENT; | |
900 | + return -ENOMEDIUM; | |
877 | 901 | if (!drv->bdrv_get_info) |
878 | 902 | return -ENOTSUP; |
879 | 903 | memset(bdi, 0, sizeof(*bdi)); |
... | ... | @@ -888,7 +912,7 @@ int bdrv_snapshot_create(BlockDriverState *bs, |
888 | 912 | { |
889 | 913 | BlockDriver *drv = bs->drv; |
890 | 914 | if (!drv) |
891 | - return -ENOENT; | |
915 | + return -ENOMEDIUM; | |
892 | 916 | if (!drv->bdrv_snapshot_create) |
893 | 917 | return -ENOTSUP; |
894 | 918 | return drv->bdrv_snapshot_create(bs, sn_info); |
... | ... | @@ -899,7 +923,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, |
899 | 923 | { |
900 | 924 | BlockDriver *drv = bs->drv; |
901 | 925 | if (!drv) |
902 | - return -ENOENT; | |
926 | + return -ENOMEDIUM; | |
903 | 927 | if (!drv->bdrv_snapshot_goto) |
904 | 928 | return -ENOTSUP; |
905 | 929 | return drv->bdrv_snapshot_goto(bs, snapshot_id); |
... | ... | @@ -909,7 +933,7 @@ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) |
909 | 933 | { |
910 | 934 | BlockDriver *drv = bs->drv; |
911 | 935 | if (!drv) |
912 | - return -ENOENT; | |
936 | + return -ENOMEDIUM; | |
913 | 937 | if (!drv->bdrv_snapshot_delete) |
914 | 938 | return -ENOTSUP; |
915 | 939 | return drv->bdrv_snapshot_delete(bs, snapshot_id); |
... | ... | @@ -920,7 +944,7 @@ int bdrv_snapshot_list(BlockDriverState *bs, |
920 | 944 | { |
921 | 945 | BlockDriver *drv = bs->drv; |
922 | 946 | if (!drv) |
923 | - return -ENOENT; | |
947 | + return -ENOMEDIUM; | |
924 | 948 | if (!drv->bdrv_snapshot_list) |
925 | 949 | return -ENOTSUP; |
926 | 950 | return drv->bdrv_snapshot_list(bs, psn_info); |
... | ... | @@ -1001,7 +1025,7 @@ BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num, |
1001 | 1025 | { |
1002 | 1026 | BlockDriver *drv = bs->drv; |
1003 | 1027 | |
1004 | - if (!bs->inserted) | |
1028 | + if (!drv) | |
1005 | 1029 | return NULL; |
1006 | 1030 | |
1007 | 1031 | /* XXX: we assume that nb_sectors == 0 is suppored by the async read */ |
... | ... | @@ -1021,7 +1045,7 @@ BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num, |
1021 | 1045 | { |
1022 | 1046 | BlockDriver *drv = bs->drv; |
1023 | 1047 | |
1024 | - if (!bs->inserted) | |
1048 | + if (!drv) | |
1025 | 1049 | return NULL; |
1026 | 1050 | if (bs->read_only) |
1027 | 1051 | return NULL; |
... | ... | @@ -1170,6 +1194,7 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, |
1170 | 1194 | void bdrv_init(void) |
1171 | 1195 | { |
1172 | 1196 | bdrv_register(&bdrv_raw); |
1197 | + bdrv_register(&bdrv_host_device); | |
1173 | 1198 | #ifndef _WIN32 |
1174 | 1199 | bdrv_register(&bdrv_cow); |
1175 | 1200 | #endif |
... | ... | @@ -1211,3 +1236,78 @@ void qemu_aio_release(void *p) |
1211 | 1236 | acb->next = drv->free_aiocb; |
1212 | 1237 | drv->free_aiocb = acb; |
1213 | 1238 | } |
1239 | + | |
1240 | +/**************************************************************/ | |
1241 | +/* removable device support */ | |
1242 | + | |
1243 | +/** | |
1244 | + * Return TRUE if the media is present | |
1245 | + */ | |
1246 | +int bdrv_is_inserted(BlockDriverState *bs) | |
1247 | +{ | |
1248 | + BlockDriver *drv = bs->drv; | |
1249 | + int ret; | |
1250 | + if (!drv) | |
1251 | + return 0; | |
1252 | + if (!drv->bdrv_is_inserted) | |
1253 | + return 1; | |
1254 | + ret = drv->bdrv_is_inserted(bs); | |
1255 | + return ret; | |
1256 | +} | |
1257 | + | |
1258 | +/** | |
1259 | + * Return TRUE if the media changed since the last call to this | |
1260 | + * function. It is currently only used for floppy disks | |
1261 | + */ | |
1262 | +int bdrv_media_changed(BlockDriverState *bs) | |
1263 | +{ | |
1264 | + BlockDriver *drv = bs->drv; | |
1265 | + int ret; | |
1266 | + | |
1267 | + if (!drv || !drv->bdrv_media_changed) | |
1268 | + ret = -ENOTSUP; | |
1269 | + else | |
1270 | + ret = drv->bdrv_media_changed(bs); | |
1271 | + if (ret == -ENOTSUP) | |
1272 | + ret = bs->media_changed; | |
1273 | + bs->media_changed = 0; | |
1274 | + return ret; | |
1275 | +} | |
1276 | + | |
1277 | +/** | |
1278 | + * If eject_flag is TRUE, eject the media. Otherwise, close the tray | |
1279 | + */ | |
1280 | +void bdrv_eject(BlockDriverState *bs, int eject_flag) | |
1281 | +{ | |
1282 | + BlockDriver *drv = bs->drv; | |
1283 | + int ret; | |
1284 | + | |
1285 | + if (!drv || !drv->bdrv_eject) { | |
1286 | + ret = -ENOTSUP; | |
1287 | + } else { | |
1288 | + ret = drv->bdrv_eject(bs, eject_flag); | |
1289 | + } | |
1290 | + if (ret == -ENOTSUP) { | |
1291 | + if (eject_flag) | |
1292 | + bdrv_close(bs); | |
1293 | + } | |
1294 | +} | |
1295 | + | |
1296 | +int bdrv_is_locked(BlockDriverState *bs) | |
1297 | +{ | |
1298 | + return bs->locked; | |
1299 | +} | |
1300 | + | |
1301 | +/** | |
1302 | + * Lock or unlock the media (if it is locked, the user won't be able | |
1303 | + * to eject it manually). | |
1304 | + */ | |
1305 | +void bdrv_set_locked(BlockDriverState *bs, int locked) | |
1306 | +{ | |
1307 | + BlockDriver *drv = bs->drv; | |
1308 | + | |
1309 | + bs->locked = locked; | |
1310 | + if (drv && drv->bdrv_set_locked) { | |
1311 | + drv->bdrv_set_locked(bs, locked); | |
1312 | + } | |
1313 | +} | ... | ... |
block_int.h
... | ... | @@ -70,6 +70,12 @@ struct BlockDriver { |
70 | 70 | QEMUSnapshotInfo **psn_info); |
71 | 71 | int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); |
72 | 72 | |
73 | + /* removable device specific */ | |
74 | + int (*bdrv_is_inserted)(BlockDriverState *bs); | |
75 | + int (*bdrv_media_changed)(BlockDriverState *bs); | |
76 | + int (*bdrv_eject)(BlockDriverState *bs, int eject_flag); | |
77 | + int (*bdrv_set_locked)(BlockDriverState *bs, int locked); | |
78 | + | |
73 | 79 | BlockDriverAIOCB *free_aiocb; |
74 | 80 | struct BlockDriver *next; |
75 | 81 | }; |
... | ... | @@ -78,7 +84,6 @@ struct BlockDriverState { |
78 | 84 | int64_t total_sectors; /* if we are reading a disk image, give its |
79 | 85 | size in sectors */ |
80 | 86 | int read_only; /* if true, the media is read only */ |
81 | - int inserted; /* if true, the media is present */ | |
82 | 87 | int removable; /* if true, the media can be removed */ |
83 | 88 | int locked; /* if true, the media cannot temporarily be ejected */ |
84 | 89 | int encrypted; /* if true, the media is encrypted */ |
... | ... | @@ -86,7 +91,7 @@ struct BlockDriverState { |
86 | 91 | void (*change_cb)(void *opaque); |
87 | 92 | void *change_opaque; |
88 | 93 | |
89 | - BlockDriver *drv; | |
94 | + BlockDriver *drv; /* NULL means no media */ | |
90 | 95 | void *opaque; |
91 | 96 | |
92 | 97 | int boot_sector_enabled; |
... | ... | @@ -96,7 +101,8 @@ struct BlockDriverState { |
96 | 101 | char backing_file[1024]; /* if non zero, the image is a diff of |
97 | 102 | this file image */ |
98 | 103 | int is_temporary; |
99 | - | |
104 | + int media_changed; | |
105 | + | |
100 | 106 | BlockDriverState *backing_hd; |
101 | 107 | /* async read/write emulation */ |
102 | 108 | ... | ... |
qemu-doc.texi
... | ... | @@ -206,7 +206,7 @@ Select the emulated machine (@code{-M ?} for list) |
206 | 206 | @item -fda file |
207 | 207 | @item -fdb file |
208 | 208 | Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can |
209 | -use the host floppy by using @file{/dev/fd0} as filename. | |
209 | +use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}). | |
210 | 210 | |
211 | 211 | @item -hda file |
212 | 212 | @item -hdb file |
... | ... | @@ -217,7 +217,7 @@ Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}). |
217 | 217 | @item -cdrom file |
218 | 218 | Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and and |
219 | 219 | @option{-cdrom} at the same time). You can use the host CD-ROM by |
220 | -using @file{/dev/cdrom} as filename. | |
220 | +using @file{/dev/cdrom} as filename (@pxref{host_drives}). | |
221 | 221 | |
222 | 222 | @item -boot [a|c|d] |
223 | 223 | Boot on floppy (a), hard disk (c) or CD-ROM (d). Hard disk boot is |
... | ... | @@ -916,6 +916,7 @@ snapshots. |
916 | 916 | * disk_images_snapshot_mode:: Snapshot mode |
917 | 917 | * vm_snapshots:: VM snapshots |
918 | 918 | * qemu_img_invocation:: qemu-img Invocation |
919 | +* host_drives:: Using host drives | |
919 | 920 | * disk_images_fat_images:: Virtual FAT disk images |
920 | 921 | @end menu |
921 | 922 | |
... | ... | @@ -997,6 +998,57 @@ state is not saved or restored properly (in particular USB). |
997 | 998 | |
998 | 999 | @include qemu-img.texi |
999 | 1000 | |
1001 | +@node host_drives | |
1002 | +@subsection Using host drives | |
1003 | + | |
1004 | +In addition to disk image files, QEMU can directly access host | |
1005 | +devices. We describe here the usage for QEMU version >= 0.8.3. | |
1006 | + | |
1007 | +@subsubsection Linux | |
1008 | + | |
1009 | +On Linux, you can directly use the host device filename instead of a | |
1010 | +disk image filename provided you have enough proviledge to access | |
1011 | +it. For example, use @file{/dev/cdrom} to access to the CDROM or | |
1012 | +@file{/dev/fd0} for the floppy. | |
1013 | + | |
1014 | +@table | |
1015 | +@item CD | |
1016 | +You can specify a CDROM device even if no CDROM is loaded. QEMU has | |
1017 | +specific code to detect CDROM insertion or removal. CDROM ejection by | |
1018 | +the guest OS is supported. Currently only data CDs are supported. | |
1019 | +@item Floppy | |
1020 | +You can specify a floppy device even if no floppy is loaded. Floppy | |
1021 | +removal is currently not detected accurately (if you change floppy | |
1022 | +without doing floppy access while the floppy is not loaded, the guest | |
1023 | +OS will think that the same floppy is loaded). | |
1024 | +@item Hard disks | |
1025 | +Hard disks can be used. Normally you must specify the whole disk | |
1026 | +(@file{/dev/hdb} instead of @file{/dev/hdb1}) so that the guest OS can | |
1027 | +see it as a partitioned disk. WARNING: unless you know what you do, it | |
1028 | +is better to only make READ-ONLY accesses to the hard disk otherwise | |
1029 | +you may corrupt your host data (use the @option{-snapshot} command | |
1030 | +line option or modify the device permissions accordingly). | |
1031 | +@end table | |
1032 | + | |
1033 | +@subsubsection Windows | |
1034 | + | |
1035 | +On Windows you can use any host drives as QEMU drive. The prefered | |
1036 | +syntax is the driver letter (e.g. @file{d:}). The alternate syntax | |
1037 | +@file{\\.\d:} is supported. @file{/dev/cdrom} is supported as an alias | |
1038 | +to the first CDROM drive. | |
1039 | + | |
1040 | +Currently there is no specific code to handle removable medias, so it | |
1041 | +is better to use the @code{change} or @code{eject} monitor commands to | |
1042 | +change or eject media. | |
1043 | + | |
1044 | +@subsubsection Mac OS X | |
1045 | + | |
1046 | +@file{/dev/cdrom} is an alias to the first CDROM. | |
1047 | + | |
1048 | +Currently there is no specific code to handle removable medias, so it | |
1049 | +is better to use the @code{change} or @code{eject} monitor commands to | |
1050 | +change or eject media. | |
1051 | + | |
1000 | 1052 | @node disk_images_fat_images |
1001 | 1053 | @subsection Virtual FAT disk images |
1002 | 1054 | ... | ... |
vl.h
... | ... | @@ -50,6 +50,7 @@ |
50 | 50 | #define fsync _commit |
51 | 51 | #define lseek _lseeki64 |
52 | 52 | #define ENOTSUP 4096 |
53 | +#define ENOMEDIUM 4097 | |
53 | 54 | extern int qemu_ftruncate64(int, int64_t); |
54 | 55 | #define ftruncate qemu_ftruncate64 |
55 | 56 | |
... | ... | @@ -502,6 +503,7 @@ typedef struct BlockDriverState BlockDriverState; |
502 | 503 | typedef struct BlockDriver BlockDriver; |
503 | 504 | |
504 | 505 | extern BlockDriver bdrv_raw; |
506 | +extern BlockDriver bdrv_host_device; | |
505 | 507 | extern BlockDriver bdrv_cow; |
506 | 508 | extern BlockDriver bdrv_qcow; |
507 | 509 | extern BlockDriver bdrv_vmdk; |
... | ... | @@ -604,8 +606,10 @@ int bdrv_get_translation_hint(BlockDriverState *bs); |
604 | 606 | int bdrv_is_removable(BlockDriverState *bs); |
605 | 607 | int bdrv_is_read_only(BlockDriverState *bs); |
606 | 608 | int bdrv_is_inserted(BlockDriverState *bs); |
609 | +int bdrv_media_changed(BlockDriverState *bs); | |
607 | 610 | int bdrv_is_locked(BlockDriverState *bs); |
608 | 611 | void bdrv_set_locked(BlockDriverState *bs, int locked); |
612 | +void bdrv_eject(BlockDriverState *bs, int eject_flag); | |
609 | 613 | void bdrv_set_change_cb(BlockDriverState *bs, |
610 | 614 | void (*change_cb)(void *opaque), void *opaque); |
611 | 615 | void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); | ... | ... |