Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Eject CDs |
| 3 | * |
| 4 | * Copyright 2005 Alexandre Julliard for CodeWeavers |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library 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 GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
Jonathan Ernst | 360a3f9 | 2006-05-18 14:49:52 +0200 | [diff] [blame] | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 19 | */ |
| 20 | |
Mike McCormack | 7d66567 | 2006-01-16 20:41:34 +0100 | [diff] [blame] | 21 | #define WIN32_LEAN_AND_MEAN |
| 22 | |
Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 23 | #include "config.h" |
| 24 | |
| 25 | #include <windows.h> |
| 26 | #include <winioctl.h> |
| 27 | #include <ntddstor.h> |
| 28 | #include <stdio.h> |
| 29 | #include <stdlib.h> |
| 30 | |
| 31 | #include "wine/debug.h" |
| 32 | |
| 33 | WINE_DEFAULT_DEBUG_CHANNEL(eject); |
| 34 | |
| 35 | /* options */ |
| 36 | static int unmount_only; |
| 37 | static int eject_all; |
| 38 | |
| 39 | /* wrapper for GetDriveTypeW */ |
| 40 | static DWORD get_drive_type( WCHAR drive ) |
| 41 | { |
| 42 | static const WCHAR rootW[] = {'a',':','\\',0}; |
| 43 | WCHAR path[16]; |
| 44 | |
| 45 | memcpy( path, rootW, sizeof(rootW) ); |
| 46 | path[0] = drive; |
| 47 | return GetDriveTypeW( path ); |
| 48 | } |
| 49 | |
| 50 | static BOOL eject_cd( WCHAR drive ) |
| 51 | { |
| 52 | static const WCHAR deviceW[] = {'\\','\\','.','\\','a',':',0}; |
| 53 | PREVENT_MEDIA_REMOVAL removal; |
| 54 | WCHAR buffer[16]; |
| 55 | HANDLE handle; |
| 56 | DWORD result; |
| 57 | |
| 58 | if (get_drive_type( drive ) != DRIVE_CDROM) |
| 59 | { |
| 60 | WINE_MESSAGE( "Drive %c: is not a CD or is not mounted\n", (char)drive ); |
| 61 | return FALSE; |
| 62 | } |
| 63 | |
| 64 | memcpy( buffer, deviceW, sizeof(deviceW) ); |
| 65 | buffer[4] = drive; |
| 66 | handle = CreateFileW( buffer, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, |
| 67 | NULL, OPEN_EXISTING, 0, 0 ); |
| 68 | if (handle == INVALID_HANDLE_VALUE) |
| 69 | { |
| 70 | WINE_MESSAGE( "Cannot open device for drive %c:\n", (char)drive ); |
| 71 | return FALSE; |
| 72 | } |
| 73 | |
| 74 | WINE_TRACE( "ejecting %c:\n", (char)drive ); |
| 75 | |
| 76 | if (!DeviceIoControl( handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &result, NULL )) |
Michael Stefaniuc | fe23cd2 | 2006-10-02 23:21:17 +0200 | [diff] [blame] | 77 | WINE_WARN( "FSCTL_DISMOUNT_VOLUME failed with err %d\n", GetLastError() ); |
Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 78 | |
| 79 | removal.PreventMediaRemoval = FALSE; |
| 80 | if (!DeviceIoControl( handle, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL, 0, &result, NULL )) |
Michael Stefaniuc | fe23cd2 | 2006-10-02 23:21:17 +0200 | [diff] [blame] | 81 | WINE_WARN( "IOCTL_STORAGE_MEDIA_REMOVAL failed with err %d\n", GetLastError() ); |
Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 82 | |
| 83 | if (!unmount_only) |
| 84 | { |
| 85 | if (!DeviceIoControl( handle, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &result, NULL )) |
Michael Stefaniuc | fe23cd2 | 2006-10-02 23:21:17 +0200 | [diff] [blame] | 86 | WINE_WARN( "IOCTL_STORAGE_EJECT_MEDIA failed with err %d\n", GetLastError() ); |
Alexandre Julliard | 5f96333 | 2005-10-31 21:10:38 +0000 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | CloseHandle( handle ); |
| 90 | return TRUE; |
| 91 | } |
| 92 | |
| 93 | /* find the CD drive, and die if we find more than one */ |
| 94 | static WCHAR find_cd_drive(void) |
| 95 | { |
| 96 | WCHAR ret = 0, drive; |
| 97 | |
| 98 | for (drive = 'c'; drive <= 'z'; drive++) |
| 99 | { |
| 100 | if (get_drive_type( drive ) != DRIVE_CDROM) continue; |
| 101 | if (ret) |
| 102 | { |
| 103 | WINE_MESSAGE( "Multiple CD drives found (%c: and %c:), you need to specify the one you want.\n", |
| 104 | (char)ret, (char)drive ); |
| 105 | exit(1); |
| 106 | } |
| 107 | ret = drive; |
| 108 | } |
| 109 | return ret; |
| 110 | } |
| 111 | |
| 112 | static void usage(void) |
| 113 | { |
| 114 | WINE_MESSAGE( "Usage: eject [-u] [-a] [-h] [x:]...\n" ); |
| 115 | WINE_MESSAGE( " -a Eject all the CD drives we find\n" ); |
| 116 | WINE_MESSAGE( " -h Display this help message\n" ); |
| 117 | WINE_MESSAGE( " -u Unmount only, don't eject the CD\n" ); |
| 118 | WINE_MESSAGE( " x: Eject drive x:\n" ); |
| 119 | exit(1); |
| 120 | } |
| 121 | |
| 122 | static void parse_options( int *argc, char *argv[] ) |
| 123 | { |
| 124 | int i; |
| 125 | char *opt; |
| 126 | |
| 127 | for (i = 1; i < *argc; i++) |
| 128 | { |
| 129 | if (argv[i][0] != '-') |
| 130 | { |
| 131 | /* check for valid drive argument */ |
| 132 | if (strlen(argv[i]) != 2 || argv[i][1] != ':') usage(); |
| 133 | continue; |
| 134 | } |
| 135 | for (opt = argv[i] + 1; *opt; opt++) switch(*opt) |
| 136 | { |
| 137 | case 'a': eject_all = 1; break; |
| 138 | case 'u': unmount_only = 1; break; |
| 139 | case 'h': usage(); break; |
| 140 | default: |
| 141 | WINE_MESSAGE( "Unknown option -%c\n", *opt ); |
| 142 | usage(); |
| 143 | } |
| 144 | memmove( argv + i, argv + i + 1, (*argc - i) * sizeof(*argv) ); |
| 145 | (*argc)--; |
| 146 | i--; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | int main( int argc, char *argv[] ) |
| 151 | { |
| 152 | parse_options( &argc, argv ); |
| 153 | |
| 154 | if (eject_all) |
| 155 | { |
| 156 | WCHAR drive; |
| 157 | |
| 158 | for (drive = 'c'; drive <= 'z'; drive++) |
| 159 | { |
| 160 | if (get_drive_type( drive ) != DRIVE_CDROM) continue; |
| 161 | if (!eject_cd( drive )) exit(1); |
| 162 | } |
| 163 | } |
| 164 | else if (argc > 1) |
| 165 | { |
| 166 | int i; |
| 167 | |
| 168 | for (i = 1; i < argc; i++) |
| 169 | if (!eject_cd( argv[i][0] )) exit(1); |
| 170 | } |
| 171 | else |
| 172 | { |
| 173 | WCHAR drive = find_cd_drive(); |
| 174 | |
| 175 | if (!drive) |
| 176 | { |
| 177 | WINE_MESSAGE( "No CD drive found\n" ); |
| 178 | exit(1); |
| 179 | } |
| 180 | if (!eject_cd( drive )) exit(1); |
| 181 | } |
| 182 | exit(0); |
| 183 | } |