2015-06-05 19:23:32 +08:00
// +build linux freebsd
2014-02-18 07:14:30 +08:00
package devices
import (
2014-05-31 09:30:27 +08:00
"errors"
2014-02-18 07:14:30 +08:00
"fmt"
2014-05-31 09:30:27 +08:00
"io/ioutil"
2014-02-18 07:14:30 +08:00
"os"
2014-05-31 09:30:27 +08:00
"path/filepath"
2014-02-18 07:14:30 +08:00
"syscall"
2015-06-22 10:29:59 +08:00
"github.com/opencontainers/runc/libcontainer/configs"
2014-02-18 07:14:30 +08:00
)
2014-05-31 09:30:27 +08:00
var (
2015-02-01 11:56:27 +08:00
ErrNotADevice = errors . New ( "not a device node" )
2014-05-31 09:30:27 +08:00
)
2014-09-05 07:34:34 +08:00
// Testing dependencies
var (
2014-09-05 08:00:09 +08:00
osLstat = os . Lstat
ioutilReadDir = ioutil . ReadDir
2014-09-05 07:34:34 +08:00
)
2015-04-20 10:35:51 +08:00
// Given the path to a device and it's cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
2015-02-01 11:56:27 +08:00
func DeviceFromPath ( path , permissions string ) ( * configs . Device , error ) {
2014-09-05 07:34:34 +08:00
fileInfo , err := osLstat ( path )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
}
2014-02-18 07:14:30 +08:00
var (
devType rune
2014-05-31 09:30:27 +08:00
mode = fileInfo . Mode ( )
fileModePermissionBits = os . FileMode . Perm ( mode )
2014-02-18 07:14:30 +08:00
)
switch {
2014-05-31 09:30:27 +08:00
case mode & os . ModeDevice == 0 :
2015-02-01 11:56:27 +08:00
return nil , ErrNotADevice
2014-05-31 09:30:27 +08:00
case mode & os . ModeCharDevice != 0 :
2014-02-18 07:14:30 +08:00
fileModePermissionBits |= syscall . S_IFCHR
devType = 'c'
default :
fileModePermissionBits |= syscall . S_IFBLK
devType = 'b'
}
2014-05-31 09:30:27 +08:00
stat_t , ok := fileInfo . Sys ( ) . ( * syscall . Stat_t )
2014-02-18 07:14:30 +08:00
if ! ok {
2014-05-31 09:30:27 +08:00
return nil , fmt . Errorf ( "cannot determine the device number for device %s" , path )
2014-02-18 07:14:30 +08:00
}
2014-05-31 09:30:27 +08:00
devNumber := int ( stat_t . Rdev )
2015-02-01 11:56:27 +08:00
return & configs . Device {
Type : devType ,
Path : path ,
Major : Major ( devNumber ) ,
Minor : Minor ( devNumber ) ,
Permissions : permissions ,
FileMode : fileModePermissionBits ,
Uid : stat_t . Uid ,
Gid : stat_t . Gid ,
2014-05-31 09:30:27 +08:00
} , nil
2014-02-18 07:14:30 +08:00
}
2015-02-01 11:56:27 +08:00
func HostDevices ( ) ( [ ] * configs . Device , error ) {
2015-02-04 09:44:58 +08:00
return getDevices ( "/dev" )
2014-05-31 09:30:27 +08:00
}
2015-02-04 09:44:58 +08:00
func getDevices ( path string ) ( [ ] * configs . Device , error ) {
2014-09-05 08:00:09 +08:00
files , err := ioutilReadDir ( path )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
2014-02-18 07:14:30 +08:00
}
2015-02-01 11:56:27 +08:00
out := [ ] * configs . Device { }
2014-05-31 09:30:27 +08:00
for _ , f := range files {
2014-10-25 02:04:20 +08:00
switch {
case f . IsDir ( ) :
2014-05-31 09:30:27 +08:00
switch f . Name ( ) {
2014-10-29 07:35:35 +08:00
case "pts" , "shm" , "fd" , "mqueue" :
2014-05-31 09:30:27 +08:00
continue
default :
2015-02-04 09:44:58 +08:00
sub , err := getDevices ( filepath . Join ( path , f . Name ( ) ) )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
}
out = append ( out , sub ... )
continue
}
2014-10-25 02:04:20 +08:00
case f . Name ( ) == "console" :
continue
2014-05-31 09:30:27 +08:00
}
2015-02-01 11:56:27 +08:00
device , err := DeviceFromPath ( filepath . Join ( path , f . Name ( ) ) , "rwm" )
2014-10-25 02:04:20 +08:00
if err != nil {
2015-02-01 11:56:27 +08:00
if err == ErrNotADevice {
2014-10-25 02:04:20 +08:00
continue
2014-05-31 09:30:27 +08:00
}
2014-10-25 02:04:20 +08:00
return nil , err
2014-05-31 09:30:27 +08:00
}
2014-10-25 02:04:20 +08:00
out = append ( out , device )
2014-05-31 09:30:27 +08:00
}
return out , nil
}