You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
624 lines
22 KiB
624 lines
22 KiB
# |
|
# Copyright (c) 2008-2014 Ashok P. Nadkarni |
|
# All rights reserved. |
|
# |
|
# See the file LICENSE for license |
|
|
|
namespace eval twapi { |
|
struct _PREVENT_MEDIA_REMOVAL { |
|
BOOLEAN PreventMediaRemoval; |
|
} |
|
record device_element { class_guid device_instance reserved } |
|
} |
|
|
|
interp alias {} close_devinfoset {} devinfoset_close |
|
|
|
proc twapi::rescan_devices {} { |
|
CM_Reenumerate_DevNode_Ex [CM_Locate_DevNode_Ex "" 0] 0 |
|
} |
|
|
|
|
|
# Callback invoked for device changes. |
|
# Does some processing of passed data and then invokes the |
|
# real callback script |
|
proc twapi::_device_notification_handler {id args} { |
|
variable _device_notifiers |
|
set idstr "devnotifier#$id" |
|
if {![info exists _device_notifiers($idstr)]} { |
|
# Notifications that expect a response default to "true" |
|
return 1 |
|
} |
|
set script [lindex $_device_notifiers($idstr) 1] |
|
|
|
# For volume notifications, change drive bitmask to |
|
# list of drives before passing back to script |
|
set event [lindex $args 0] |
|
if {[lindex $args 1] eq "volume" && |
|
($event eq "deviceremovecomplete" || $event eq "devicearrival")} { |
|
lset args 2 [_drivemask_to_drivelist [lindex $args 2]] |
|
|
|
# Also indicate whether network volume and whether change is a media |
|
# change or physical change |
|
set attrs [list ] |
|
set flags [lindex $args 3] |
|
if {$flags & 1} { |
|
lappend attrs mediachange |
|
} |
|
if {$flags & 2} { |
|
lappend attrs networkvolume |
|
} |
|
lset args 3 $attrs |
|
} |
|
|
|
return [uplevel #0 [linsert $script end $idstr {*}$args]] |
|
} |
|
|
|
proc twapi::start_device_notifier {script args} { |
|
variable _device_notifiers |
|
|
|
set script [lrange $script 0 end]; # Verify syntactically a list |
|
|
|
array set opts [parseargs args { |
|
deviceinterface.arg |
|
handle.arg |
|
} -maxleftover 0] |
|
|
|
# For reference - some common device interface classes |
|
# NOTE: NOT ALL HAVE BEEN VERIFIED! |
|
# Network Card {ad498944-762f-11d0-8dcb-00c04fc3358c} |
|
# Human Interface Device (HID) {4d1e55b2-f16f-11cf-88cb-001111000030} |
|
# GUID_DEVINTERFACE_DISK - {53f56307-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_CDROM - {53f56308-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_PARTITION - {53f5630a-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_TAPE - {53f5630b-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_WRITEONCEDISK - {53f5630c-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_VOLUME - {53f5630d-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_MEDIUMCHANGER - {53f56310-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_FLOPPY - {53f56311-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_CDCHANGER - {53f56312-b6bf-11d0-94f2-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_STORAGEPORT - {2accfe60-c130-11d2-b082-00a0c91efb8b} |
|
# GUID_DEVINTERFACE_KEYBOARD - {884b96c3-56ef-11d1-bc8c-00a0c91405dd} |
|
# GUID_DEVINTERFACE_MOUSE - {378de44c-56ef-11d1-bc8c-00a0c91405dd} |
|
# GUID_DEVINTERFACE_PARALLEL - {97F76EF0-F883-11D0-AF1F-0000F800845C} |
|
# GUID_DEVINTERFACE_COMPORT - {86e0d1e0-8089-11d0-9ce4-08003e301f73} |
|
# GUID_DEVINTERFACE_DISPLAY_ADAPTER - {5b45201d-f2f2-4f3b-85bb-30ff1f953599} |
|
# GUID_DEVINTERFACE_USB_HUB - {f18a0e88-c30c-11d0-8815-00a0c906bed8} |
|
# GUID_DEVINTERFACE_USB_DEVICE - {A5DCBF10-6530-11D2-901F-00C04FB951ED} |
|
# GUID_DEVINTERFACE_USB_HOST_CONTROLLER - {3abf6f2d-71c4-462a-8a92-1e6861e6af27} |
|
|
|
|
|
if {[info exists opts(deviceinterface)] && [info exists opts(handle)]} { |
|
error "Options -deviceinterface and -handle are mutually exclusive." |
|
} |
|
|
|
if {![info exists opts(deviceinterface)]} { |
|
set opts(deviceinterface) "" |
|
} |
|
if {[info exists opts(handle)]} { |
|
set type 6 |
|
} else { |
|
set opts(handle) NULL |
|
switch -exact -- $opts(deviceinterface) { |
|
port { set type 3 ; set opts(deviceinterface) "" } |
|
volume { set type 2 ; set opts(deviceinterface) "" } |
|
default { |
|
# device interface class guid or empty string (for all device interfaces) |
|
set type 5 |
|
} |
|
} |
|
} |
|
|
|
set id [Twapi_RegisterDeviceNotification $type $opts(deviceinterface) $opts(handle)] |
|
set idstr "devnotifier#$id" |
|
|
|
set _device_notifiers($idstr) [list $id $script] |
|
return $idstr |
|
} |
|
|
|
proc twapi::stop_device_notifier {idstr} { |
|
variable _device_notifiers |
|
|
|
if {![info exists _device_notifiers($idstr)]} { |
|
return; |
|
} |
|
|
|
Twapi_UnregisterDeviceNotification [lindex $_device_notifiers($idstr) 0] |
|
unset _device_notifiers($idstr) |
|
} |
|
|
|
proc twapi::devinfoset {args} { |
|
array set opts [parseargs args { |
|
{guid.arg ""} |
|
{classtype.arg setup {interface setup}} |
|
{presentonly.bool false 0x2} |
|
{currentprofileonly.bool false 0x8} |
|
{deviceinfoset.arg NULL} |
|
{hwin.int 0} |
|
{system.arg ""} |
|
{pnpenumerator.arg ""} |
|
} -maxleftover 0] |
|
|
|
# DIGCF_ALLCLASSES is bitmask 4 |
|
set flags [expr {$opts(guid) eq "" ? 0x4 : 0}] |
|
if {$opts(classtype) eq "interface"} { |
|
if {$opts(pnpenumerator) ne ""} { |
|
error "The -pnpenumerator option cannot be used when -classtype interface is specified." |
|
} |
|
# DIGCF_DEVICEINTERFACE |
|
set flags [expr {$flags | 0x10}] |
|
} |
|
|
|
# DIGCF_PRESENT |
|
set flags [expr {$flags | $opts(presentonly)}] |
|
|
|
# DIGCF_PRESENT |
|
set flags [expr {$flags | $opts(currentprofileonly)}] |
|
|
|
return [SetupDiGetClassDevsEx \ |
|
$opts(guid) \ |
|
$opts(pnpenumerator) \ |
|
$opts(hwin) \ |
|
$flags \ |
|
$opts(deviceinfoset) \ |
|
$opts(system)] |
|
} |
|
|
|
|
|
# Given a device information set, returns the device elements within it |
|
proc twapi::devinfoset_elements {hdevinfo} { |
|
set result [list ] |
|
set i 0 |
|
trap { |
|
while {true} { |
|
lappend result [SetupDiEnumDeviceInfo $hdevinfo $i] |
|
incr i |
|
} |
|
} onerror {TWAPI_WIN32 0x103} { |
|
# Fine, Just means no more items |
|
} onerror {TWAPI_WIN32 0x80070103} { |
|
# Fine, Just means no more items (HRESULT version of above code) |
|
} |
|
|
|
return $result |
|
} |
|
|
|
# Given a device information set, returns the device elements within it |
|
proc twapi::devinfoset_instance_ids {hdevinfo} { |
|
set result [list ] |
|
set i 0 |
|
trap { |
|
while {true} { |
|
lappend result [device_element_instance_id $hdevinfo [SetupDiEnumDeviceInfo $hdevinfo $i]] |
|
incr i |
|
} |
|
} onerror {TWAPI_WIN32 0x103} { |
|
# Fine, Just means no more items |
|
} onerror {TWAPI_WIN32 0x80070103} { |
|
# Fine, Just means no more items (HRESULT version of above code) |
|
} |
|
|
|
return $result |
|
} |
|
|
|
# Returns a device instance element from a devinfoset |
|
proc twapi::devinfoset_element {hdevinfo instance_id} { |
|
return [SetupDiOpenDeviceInfo $hdevinfo $instance_id 0 0] |
|
} |
|
|
|
# Get the registry property for a devinfoset element |
|
proc twapi::devinfoset_element_registry_property {hdevinfo develem prop} { |
|
Twapi_SetupDiGetDeviceRegistryProperty $hdevinfo $develem [_device_registry_sym_to_code $prop] |
|
} |
|
|
|
# Given a device information set, returns a list of specified registry |
|
# properties for all elements of the set |
|
# args is list of properties to retrieve |
|
proc twapi::devinfoset_registry_properties {hdevinfo args} { |
|
set result [list ] |
|
trap { |
|
# Keep looping until there is an error saying no more items |
|
set i 0 |
|
while {true} { |
|
|
|
# First element is the DEVINFO_DATA element |
|
set devinfo_data [SetupDiEnumDeviceInfo $hdevinfo $i] |
|
set item [list -deviceelement $devinfo_data ] |
|
|
|
# Get all specified property values |
|
foreach prop $args { |
|
set intprop [_device_registry_sym_to_code $prop] |
|
trap { |
|
lappend item $prop \ |
|
[list success \ |
|
[Twapi_SetupDiGetDeviceRegistryProperty \ |
|
$hdevinfo $devinfo_data $intprop]] |
|
} onerror {} { |
|
lappend item $prop [list fail [list [trapresult] $::errorCode]] |
|
} |
|
} |
|
lappend result $item |
|
|
|
incr i |
|
} |
|
} onerror {TWAPI_WIN32 0x103} { |
|
# Fine, Just means no more items |
|
} onerror {TWAPI_WIN32 0x80070103} { |
|
# Fine, Just means no more items (HRESULT version of above code) |
|
} |
|
|
|
return $result |
|
} |
|
|
|
|
|
# Given a device information set, returns specified device interface |
|
# properties |
|
# TBD - document ? |
|
proc twapi::devinfoset_interface_details {hdevinfo guid args} { |
|
set result [list ] |
|
|
|
array set opts [parseargs args { |
|
{matchdeviceelement.arg {}} |
|
interfaceclass |
|
flags |
|
devicepath |
|
deviceelement |
|
ignoreerrors |
|
} -maxleftover 0] |
|
|
|
trap { |
|
# Keep looping until there is an error saying no more items |
|
set i 0 |
|
while {true} { |
|
set interface_data [SetupDiEnumDeviceInterfaces $hdevinfo \ |
|
$opts(matchdeviceelement) $guid $i] |
|
set item [list ] |
|
if {$opts(interfaceclass)} { |
|
lappend item -interfaceclass [lindex $interface_data 0] |
|
} |
|
if {$opts(flags)} { |
|
set flags [lindex $interface_data 1] |
|
set symflags [_make_symbolic_bitmask $flags {active 1 default 2 removed 4} false] |
|
lappend item -flags [linsert $symflags 0 $flags] |
|
} |
|
|
|
if {$opts(devicepath) || $opts(deviceelement)} { |
|
# Need to get device interface detail. |
|
trap { |
|
foreach {devicepath deviceelement} \ |
|
[SetupDiGetDeviceInterfaceDetail \ |
|
$hdevinfo \ |
|
$interface_data \ |
|
$opts(matchdeviceelement)] \ |
|
break |
|
|
|
if {$opts(deviceelement)} { |
|
lappend item -deviceelement $deviceelement |
|
} |
|
if {$opts(devicepath)} { |
|
lappend item -devicepath $devicepath |
|
} |
|
} onerror {} { |
|
if {! $opts(ignoreerrors)} { |
|
rethrow |
|
} |
|
} |
|
} |
|
lappend result $item |
|
|
|
incr i |
|
} |
|
} onerror {TWAPI_WIN32 0x103} { |
|
# Fine, Just means no more items |
|
} onerror {TWAPI_WIN32 0x80070103} { |
|
# Fine, Just means no more items (HRESULT version of above code) |
|
} |
|
|
|
return $result |
|
} |
|
|
|
|
|
# Return the guids associated with a device class set name. Note |
|
# the latter is not unique so multiple guids may be associated. |
|
proc twapi::device_setup_class_name_to_guids {name args} { |
|
array set opts [parseargs args { |
|
system.arg |
|
} -maxleftover 0 -nulldefault] |
|
|
|
return [twapi::SetupDiClassGuidsFromNameEx $name $opts(system)] |
|
} |
|
|
|
# Utility functions |
|
|
|
proc twapi::_init_device_registry_code_maps {} { |
|
variable _device_registry_syms |
|
variable _device_registry_codes |
|
|
|
# Note this list is ordered based on the corresponding integer codes |
|
set _device_registry_code_syms { |
|
devicedesc hardwareid compatibleids unused0 service unused1 |
|
unused2 class classguid driver configflags mfg friendlyname |
|
location_information physical_device_object_name capabilities |
|
ui_number upperfilters lowerfilters |
|
bustypeguid legacybustype busnumber enumerator_name security |
|
security_sds devtype exclusive characteristics address |
|
ui_number_desc_format device_power_data |
|
removal_policy removal_policy_hw_default removal_policy_override |
|
install_state location_paths base_containerid |
|
} |
|
|
|
set i 0 |
|
foreach sym $_device_registry_code_syms { |
|
set _device_registry_codes($sym) $i |
|
incr i |
|
} |
|
} |
|
|
|
# Map a device registry property to a symbol |
|
proc twapi::_device_registry_code_to_sym {code} { |
|
_init_device_registry_code_maps |
|
|
|
# Once we have initialized, redefine ourselves so we do not do so |
|
# every time. Note define at global ::twapi scope! |
|
proc ::twapi::_device_registry_code_to_sym {code} { |
|
variable _device_registry_code_syms |
|
if {$code >= [llength $_device_registry_code_syms]} { |
|
return $code |
|
} else { |
|
return [lindex $_device_registry_code_syms $code] |
|
} |
|
} |
|
# Call the redefined proc |
|
return [_device_registry_code_to_sym $code] |
|
} |
|
|
|
# Map a device registry property symbol to a numeric code |
|
proc twapi::_device_registry_sym_to_code {sym} { |
|
_init_device_registry_code_maps |
|
|
|
# Once we have initialized, redefine ourselves so we do not do so |
|
# every time. Note define at global ::twapi scope! |
|
proc ::twapi::_device_registry_sym_to_code {sym} { |
|
variable _device_registry_codes |
|
# Return the value. If non-existent, an error will be raised |
|
if {[info exists _device_registry_codes($sym)]} { |
|
return $_device_registry_codes($sym) |
|
} elseif {[string is integer -strict $sym]} { |
|
return $sym |
|
} else { |
|
error "Unknown or unsupported device registry property symbol '$sym'" |
|
} |
|
} |
|
# Call the redefined proc |
|
return [_device_registry_sym_to_code $sym] |
|
} |
|
|
|
# Do a device ioctl, returning result as a binary |
|
# TBD - document that caller has to handle errors 122 (ERROR_INSUFFICIENT_BUFFER) and (ERROR_MORE_DATA) |
|
proc twapi::device_ioctl {h code args} { |
|
array set opts [parseargs args { |
|
{input.arg {}} |
|
{outputcount.int 0} |
|
} -maxleftover 0] |
|
|
|
return [DeviceIoControl $h $code $opts(input) $opts(outputcount)] |
|
} |
|
|
|
|
|
# Return a list of physical disks. Note CD-ROMs and floppies not included |
|
proc twapi::find_physical_disks {} { |
|
# Disk interface class guid |
|
set guid {{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}} |
|
set hdevinfo [devinfoset \ |
|
-guid $guid \ |
|
-presentonly true \ |
|
-classtype interface] |
|
trap { |
|
return [kl_flatten [devinfoset_interface_details $hdevinfo $guid -devicepath] -devicepath] |
|
} finally { |
|
devinfoset_close $hdevinfo |
|
} |
|
} |
|
|
|
# Return information about a physical disk |
|
proc twapi::get_physical_disk_info {disk args} { |
|
set result [list ] |
|
|
|
array set opts [parseargs args { |
|
geometry |
|
layout |
|
all |
|
} -maxleftover 0] |
|
|
|
if {$opts(all) || $opts(geometry) || $opts(layout)} { |
|
set h [create_file $disk -createdisposition open_existing] |
|
} |
|
|
|
trap { |
|
if {$opts(all) || $opts(geometry)} { |
|
# IOCTL_DISK_GET_DRIVE_GEOMETRY - 0x70000 |
|
if {[binary scan [device_ioctl $h 0x70000 -outputcount 24] "wiiii" geom(-cylinders) geom(-mediatype) geom(-trackspercylinder) geom(-sectorspertrack) geom(-bytespersector)] != 5} { |
|
error "DeviceIoControl 0x70000 on disk '$disk' returned insufficient data." |
|
} |
|
lappend result -geometry [array get geom] |
|
} |
|
|
|
if {$opts(all) || $opts(layout)} { |
|
# XP and later - IOCTL_DISK_GET_DRIVE_LAYOUT_EX |
|
set data [device_ioctl $h 0x70050 -outputcount 624] |
|
if {[binary scan $data "i i" partstyle layout(-partitioncount)] != 2} { |
|
error "DeviceIoControl 0x70050 on disk '$disk' returned insufficient data." |
|
} |
|
set layout(-partitionstyle) [_partition_style_sym $partstyle] |
|
switch -exact -- $layout(-partitionstyle) { |
|
mbr { |
|
if {[binary scan $data "@8 i" layout(-signature)] != 1} { |
|
error "DeviceIoControl 0x70050 on disk '$disk' returned insufficient data." |
|
} |
|
} |
|
gpt { |
|
set pi(-diskid) [_binary_to_guid $data 32] |
|
if {[binary scan $data "@8 w w i" layout(-startingusableoffset) layout(-usablelength) layout(-maxpartitioncount)] != 3} { |
|
error "DeviceIoControl 0x70050 on disk '$disk' returned insufficient data." |
|
} |
|
} |
|
raw - |
|
unknown { |
|
# No fields to add |
|
} |
|
} |
|
|
|
set layout(-partitions) [list ] |
|
for {set i 0} {$i < $layout(-partitioncount)} {incr i} { |
|
# Decode each partition in turn. Sizeof of PARTITION_INFORMATION_EX is 144 |
|
lappend layout(-partitions) [_decode_PARTITION_INFORMATION_EX_binary $data [expr {48 + (144*$i)}]] |
|
} |
|
lappend result -layout [array get layout] |
|
} |
|
|
|
} finally { |
|
if {[info exists h]} { |
|
CloseHandle $h |
|
} |
|
} |
|
|
|
return $result |
|
} |
|
|
|
# Given a Tcl binary and offset, decode the PARTITION_INFORMATION_EX record |
|
proc twapi::_decode_PARTITION_INFORMATION_EX_binary {bin off} { |
|
if {[binary scan $bin "@$off i x4 w w i c" \ |
|
pi(-partitionstyle) \ |
|
pi(-startingoffset) \ |
|
pi(-partitionlength) \ |
|
pi(-partitionnumber) \ |
|
pi(-rewritepartition)] != 5} { |
|
error "Truncated partition structure." |
|
} |
|
|
|
set pi(-partitionstyle) [_partition_style_sym $pi(-partitionstyle)] |
|
|
|
# MBR/GPT are at offset 32 in the structure |
|
switch -exact -- $pi(-partitionstyle) { |
|
mbr { |
|
if {[binary scan $bin "@$off x32 c c c x i" pi(-partitiontype) pi(-bootindicator) pi(-recognizedpartition) pi(-hiddensectors)] != 4} { |
|
error "Truncated partition structure." |
|
} |
|
# Show partition type in hex, not negative number |
|
set pi(-partitiontype) [format 0x%2.2x [expr {0xff & $pi(-partitiontype)}]] |
|
} |
|
gpt { |
|
set pi(-partitiontype) [_binary_to_guid $bin [expr {$off+32}]] |
|
set pi(-partitionif) [_binary_to_guid $bin [expr {$off+48}]] |
|
if {[binary scan $bin "@$off x64 w" pi(-attributes)] != 1} { |
|
error "Truncated partition structure." |
|
} |
|
set pi(-name) [_ucs16_binary_to_string [string range $bin [expr {$off+72}] end]] |
|
} |
|
raw - |
|
unknown { |
|
# No fields to add |
|
} |
|
|
|
} |
|
|
|
return [array get pi] |
|
} |
|
|
|
# IOCTL_STORAGE_EJECT_MEDIA |
|
interp alias {} twapi::eject {} twapi::eject_media |
|
proc twapi::eject_media device { |
|
# http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q165721& |
|
set h [_open_disk_device $device] |
|
trap { |
|
device_ioctl $h 0x90018; # FSCTL_LOCK_VOLUME |
|
device_ioctl $h 0x90020; # FSCTL_DISMOUNT_VOLUME |
|
# IOCTL_STORAGE_MEDIA_REMOVAL (0) |
|
device_ioctl $h 0x2d4804 -input [_PREVENT_MEDIA_REMOVAL 0] |
|
device_ioctl $h 0x2d4808; # IOCTL_STORAGE_EJECT_MEDIA |
|
} finally { |
|
close_handle $h |
|
} |
|
} |
|
|
|
# IOCTL_DISK_LOAD_MEDIA |
|
# TBD - should we use IOCTL_DISK_LOAD_MEDIA2 instead (0x2d080c) see |
|
# SDK, faster if read / write access not necessary. We are closing |
|
# the handle right away anyway but would that stop other apps from |
|
# acessing the file system on the CD ? Need to try (note device |
|
# has to be opened with FILE_READ_ATTRIBUTES only in that case) |
|
|
|
interp alias {} twapi::load_media {} twapi::_issue_disk_ioctl 0x2d480c |
|
|
|
# FSCTL_LOCK_VOLUME |
|
# TBD - interp alias {} twapi::lock_volume {} twapi::_issue_disk_ioctl 0x90018 |
|
# FSCTL_LOCK_VOLUME |
|
# TBD - interp alias {} twapi::unlock_volume {} twapi::_issue_disk_ioctl 0x9001c |
|
|
|
proc twapi::_lock_media {lock device} { |
|
# IOCTL_STORAGE_MEDIA_REMOVAL |
|
_issue_disk_ioctl 0x2d4804 $device -input [_PREVENT_MEDIA_REMOVAL $lock] |
|
} |
|
interp alias {} twapi::lock_media {} twapi::_lock_media 1 |
|
interp alias {} twapi::unlock_media {} twapi::_lock_media 0 |
|
|
|
proc twapi::_issue_disk_ioctl {ioctl device args} { |
|
set h [_open_disk_device $device] |
|
trap { |
|
device_ioctl $h $ioctl {*}$args |
|
} finally { |
|
close_handle $h |
|
} |
|
} |
|
|
|
twapi::proc* twapi::_open_disk_device {device} { |
|
package require twapi_storage |
|
} { |
|
# device must be "cdrom", X:, X:\\, X:/, a volume or a physical disk as |
|
# returned from find_physical_disks |
|
switch -regexp -nocase -- $device { |
|
{^cdrom$} { |
|
foreach drive [find_logical_drives] { |
|
if {![catch {get_drive_type $drive} drive_type]} { |
|
if {$drive_type eq "cdrom"} { |
|
set device "\\\\.\\$drive" |
|
break |
|
} |
|
} |
|
} |
|
if {$device eq "cdrom"} { |
|
error "Could not find a CD-ROM device." |
|
} |
|
} |
|
{^[[:alpha:]]:(/|\\)?$} { |
|
set device "\\\\.\\[string range $device 0 1]" |
|
} |
|
{^\\\\\?\\.*#\{[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}\}$} { |
|
# Device name ok |
|
} |
|
{^\\\\\?\\Volume\{[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}\}\\?$} { |
|
# Volume name ok. But make sure we trim off any trailing |
|
# \ since create_file will open the root dir instead of the device |
|
set device [string trimright $device \\] |
|
} |
|
default { |
|
# Just to prevent us from opening some file instead |
|
error "Invalid device name '$device'" |
|
} |
|
} |
|
|
|
# http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q165721& |
|
return [create_file $device -access {generic_read generic_write} \ |
|
-createdisposition open_existing \ |
|
-share {read write}] |
|
} |
|
|
|
|
|
# Map a partition style code to a symbol |
|
proc twapi::_partition_style_sym {partstyle} { |
|
set partstyle [lindex {mbr gpt raw} $partstyle] |
|
if {$partstyle ne ""} { |
|
return $partstyle |
|
} |
|
return "unknown" |
|
} |
|
|
|
|