/* create a new dos drive */ NTSTATUS add_dos_device( int letter, const char *udi, const char *device, const char *mount_point, enum device_type type, const GUID *guid ) { char *path, *p; HKEY hkey; NTSTATUS status = STATUS_SUCCESS; struct dos_drive *drive, *next; struct volume *volume; int notify = -1; if (!(path = get_dosdevices_path( &p ))) return STATUS_NO_MEMORY; EnterCriticalSection( &device_section ); volume = find_matching_volume( udi, device, mount_point, type ); if (letter == -1) /* auto-assign a letter */ { letter = add_drive( device, type ); if (letter == -1) { status = STATUS_OBJECT_NAME_COLLISION; goto done; } LIST_FOR_EACH_ENTRY_SAFE( drive, next, &drives_list, struct dos_drive, entry ) { if (drive->volume->udi && !strcmp( udi, drive->volume->udi )) goto found; if (drive->drive == letter) delete_dos_device( drive ); } }
static BOOL set_unix_mount_point( struct dos_drive *drive, const char *mount_point ) { char *path, *p; BOOL modified = FALSE; if (!(path = get_dosdevices_path( &p ))) return FALSE; p[0] = 'a' + drive->drive; p[2] = 0; if (mount_point && mount_point[0]) { /* try to avoid unlinking if already set correctly */ if (!drive->unix_mount || strcmp( drive->unix_mount, mount_point )) { unlink( path ); symlink( mount_point, path ); modified = TRUE; } RtlFreeHeap( GetProcessHeap(), 0, drive->unix_mount ); drive->unix_mount = strdupA( mount_point ); if (drive->dosdev) set_mount_point_id( drive->dosdev, mount_point, strlen(mount_point) + 1 ); if (drive->volume) set_mount_point_id( drive->volume, mount_point, strlen(mount_point) + 1 ); } else { if (unlink( path ) != -1) modified = TRUE; RtlFreeHeap( GetProcessHeap(), 0, drive->unix_mount ); drive->unix_mount = NULL; if (drive->dosdev) set_mount_point_id( drive->dosdev, NULL, 0 ); if (drive->volume) set_mount_point_id( drive->volume, NULL, 0 ); } HeapFree( GetProcessHeap(), 0, path ); return modified; }
static void set_mount_point( struct dos_drive *drive, const char *mount_point ) { char *path, *p; struct stat path_st, mnt_st; if (drive->drive == -1) return; if (!(path = get_dosdevices_path())) return; p = path + strlen(path) - 3; *p = 'a' + drive->drive; p[2] = 0; if (mount_point[0]) { /* try to avoid unlinking if already set correctly */ if (stat( path, &path_st ) == -1 || stat( mount_point, &mnt_st ) == -1 || path_st.st_dev != mnt_st.st_dev || path_st.st_ino != mnt_st.st_ino) { unlink( path ); symlink( mount_point, path ); } } else unlink( path ); HeapFree( GetProcessHeap(), 0, path ); }
/* create devices for mapped drives */ static void create_drive_devices(void) { char *path, *p, *link, *device; struct dos_drive *drive; struct volume *volume; unsigned int i; HKEY drives_key; enum device_type drive_type; WCHAR driveW[] = {'a',':',0}; if (!(path = get_dosdevices_path( &p ))) return; if (RegOpenKeyW( HKEY_LOCAL_MACHINE, drives_keyW, &drives_key )) drives_key = 0; for (i = 0; i < MAX_DOS_DRIVES; i++) { p[0] = 'a' + i; p[2] = 0; if (!(link = read_symlink( path ))) continue; p[2] = ':'; device = read_symlink( path ); drive_type = i < 2 ? DEVICE_FLOPPY : DEVICE_HARDDISK_VOL; if (drives_key) { WCHAR buffer[32]; DWORD j, type, size = sizeof(buffer); driveW[0] = 'a' + i; if (!RegQueryValueExW( drives_key, driveW, NULL, &type, (BYTE *)buffer, &size ) && type == REG_SZ) { for (j = 0; j < sizeof(drive_types)/sizeof(drive_types[0]); j++) if (drive_types[j][0] && !strcmpiW( buffer, drive_types[j] )) { drive_type = j; break; } if (drive_type == DEVICE_FLOPPY && i >= 2) drive_type = DEVICE_HARDDISK; } } volume = find_matching_volume( NULL, device, link, drive_type ); if (!create_dos_device( volume, NULL, i, drive_type, &drive )) { /* don't reset uuid if we used an existing volume */ const GUID *guid = volume ? NULL : get_default_uuid(i); set_volume_info( drive->volume, drive, device, link, drive_type, guid ); } else { RtlFreeHeap( GetProcessHeap(), 0, link ); RtlFreeHeap( GetProcessHeap(), 0, device ); } if (volume) release_volume( volume ); } RegCloseKey( drives_key ); RtlFreeHeap( GetProcessHeap(), 0, path ); }
/* create devices for mapped drives */ static void create_drive_devices(void) { char *path, *p, *link, *device; struct dos_drive *drive; unsigned int i; HKEY drives_key; enum device_type drive_type; WCHAR driveW[] = {'a',':',0}; if (!(path = get_dosdevices_path( &p ))) return; if (RegOpenKeyW( HKEY_LOCAL_MACHINE, drives_keyW, &drives_key )) drives_key = 0; for (i = 0; i < MAX_DOS_DRIVES; i++) { p[0] = 'a' + i; p[2] = 0; if (!(link = read_symlink( path ))) continue; p[2] = ':'; device = read_symlink( path ); drive_type = i < 2 ? DEVICE_FLOPPY : DEVICE_HARDDISK_VOL; if (drives_key) { WCHAR buffer[32]; DWORD j, type, size = sizeof(buffer); driveW[0] = 'a' + i; if (!RegQueryValueExW( drives_key, driveW, NULL, &type, (BYTE *)buffer, &size ) && type == REG_SZ) { for (j = 0; j < sizeof(drive_types)/sizeof(drive_types[0]); j++) if (drive_types[j][0] && !strcmpiW( buffer, drive_types[j] )) { drive_type = j; break; } if (drive_type == DEVICE_FLOPPY && i >= 2) drive_type = DEVICE_HARDDISK; } } if (!create_disk_device( NULL, drive_type, &drive )) { drive->unix_mount = link; drive->unix_device = device; set_drive_letter( drive, i ); } else { RtlFreeHeap( GetProcessHeap(), 0, link ); RtlFreeHeap( GetProcessHeap(), 0, device ); } } RegCloseKey( drives_key ); RtlFreeHeap( GetProcessHeap(), 0, path ); }
/* find or create a DOS drive for the corresponding device */ static int add_drive( const char *device, enum device_type type ) { char *path, *p; char in_use[26]; struct stat dev_st, drive_st; int drive, first, last, avail = 0; if (stat( device, &dev_st ) == -1 || !is_valid_device( &dev_st )) return -1; if (!(path = get_dosdevices_path( &p ))) return -1; memset( in_use, 0, sizeof(in_use) ); switch (type) { case DEVICE_FLOPPY: first = 0; last = 2; break; case DEVICE_CDROM: case DEVICE_DVD: first = 3; last = 26; break; default: first = 2; last = 26; break; } while (avail != -1) { avail = -1; for (drive = first; drive < last; drive++) { if (in_use[drive]) continue; /* already checked */ *p = 'a' + drive; if (stat( path, &drive_st ) == -1) { if (lstat( path, &drive_st ) == -1 && errno == ENOENT) /* this is a candidate */ { if (avail == -1) { p[2] = 0; /* if mount point symlink doesn't exist either, it's available */ if (lstat( path, &drive_st ) == -1 && errno == ENOENT) avail = drive; p[2] = ':'; } } else in_use[drive] = 1; } else { in_use[drive] = 1; if (!is_valid_device( &drive_st )) continue; if (dev_st.st_rdev == drive_st.st_rdev) goto done; } } if (avail != -1) { /* try to use the one we found */ drive = avail; *p = 'a' + drive; if (symlink( device, path ) != -1) goto done; /* failed, retry the search */ } } drive = -1; done: HeapFree( GetProcessHeap(), 0, path ); return drive; }
/* create a new dos drive */ NTSTATUS add_dos_device( int letter, const char *udi, const char *device, const char *mount_point, enum device_type type ) { struct dos_drive *drive, *next; if (letter == -1) /* auto-assign a letter */ { letter = add_drive( device, type ); if (letter == -1) return STATUS_OBJECT_NAME_COLLISION; } else /* simply reset the device symlink */ { char *path, *p; if (!(path = get_dosdevices_path( &p ))) return STATUS_NO_MEMORY; *p = 'a' + letter; unlink( path ); if (device) symlink( device, path ); } LIST_FOR_EACH_ENTRY_SAFE( drive, next, &drives_list, struct dos_drive, entry ) { if (udi && drive->udi && !strcmp( udi, drive->udi )) { if (type == drive->type) goto found; delete_disk_device( drive ); continue; } if (drive->drive == letter) delete_disk_device( drive ); } if (create_disk_device( udi, type, &drive )) return STATUS_NO_MEMORY; found: RtlFreeHeap( GetProcessHeap(), 0, drive->unix_device ); drive->unix_device = strdupA( device ); set_drive_letter( drive, letter ); set_unix_mount_point( drive, mount_point ); if (drive->drive != -1) { HKEY hkey; TRACE( "added device %c: udi %s for %s on %s type %u\n", 'a' + drive->drive, wine_dbgstr_a(udi), wine_dbgstr_a(device), wine_dbgstr_a(mount_point), type ); /* hack: force the drive type in the registry */ if (!RegCreateKeyW( HKEY_LOCAL_MACHINE, drives_keyW, &hkey )) { const WCHAR *type_name = drive_types[type]; WCHAR name[3] = {'a',':',0}; name[0] += drive->drive; if (!type_name[0] && type == DEVICE_HARDDISK) type_name = drive_types[DEVICE_FLOPPY]; if (type_name[0]) RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)type_name, (strlenW(type_name) + 1) * sizeof(WCHAR) ); else RegDeleteValueW( hkey, name ); RegCloseKey( hkey ); } if (udi) send_notify( drive->drive, DBT_DEVICEARRIVAL ); } return STATUS_SUCCESS; }
/* find or create a DOS drive for the corresponding device */ static int add_drive( const char *device, const char *type ) { char *path, *p; char in_use[26]; struct stat dev_st, drive_st; int drive, first, last, avail = 0; if (stat( device, &dev_st ) == -1 || !S_ISBLK( dev_st.st_mode )) return -1; if (!(path = get_dosdevices_path())) return -1; p = path + strlen(path) - 3; memset( in_use, 0, sizeof(in_use) ); first = 2; last = 26; if (type && !strcmp( type, "floppy" )) { first = 0; last = 2; } while (avail != -1) { avail = -1; for (drive = first; drive < last; drive++) { if (in_use[drive]) continue; /* already checked */ *p = 'a' + drive; if (stat( path, &drive_st ) == -1) { if (lstat( path, &drive_st ) == -1 && errno == ENOENT) /* this is a candidate */ { if (avail == -1) { p[2] = 0; /* if mount point symlink doesn't exist either, it's available */ if (lstat( path, &drive_st ) == -1 && errno == ENOENT) avail = drive; p[2] = ':'; } } else in_use[drive] = 1; } else { in_use[drive] = 1; if (!S_ISBLK( drive_st.st_mode )) continue; if (dev_st.st_rdev == drive_st.st_rdev) goto done; } } if (avail != -1) { /* try to use the one we found */ drive = avail; *p = 'a' + drive; if (symlink( device, path ) != -1) goto done; /* failed, retry the search */ } } drive = -1; done: HeapFree( GetProcessHeap(), 0, path ); return drive; }