2017-02-01 00:45:59 +00:00
// +build linux freebsd
package devices
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"github.com/opencontainers/runc/libcontainer/configs"
)
var (
ErrNotADevice = errors . New ( "not a device node" )
)
// Testing dependencies
var (
osLstat = os . Lstat
ioutilReadDir = ioutil . ReadDir
)
2017-02-06 20:16:36 +00:00
// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
2017-02-01 00:45:59 +00:00
func DeviceFromPath ( path , permissions string ) ( * configs . Device , error ) {
fileInfo , err := osLstat ( path )
if err != nil {
return nil , err
}
var (
devType rune
mode = fileInfo . Mode ( )
fileModePermissionBits = os . FileMode . Perm ( mode )
)
switch {
case mode & os . ModeDevice == 0 :
return nil , ErrNotADevice
case mode & os . ModeCharDevice != 0 :
fileModePermissionBits |= syscall . S_IFCHR
devType = 'c'
default :
fileModePermissionBits |= syscall . S_IFBLK
devType = 'b'
}
stat_t , ok := fileInfo . Sys ( ) . ( * syscall . Stat_t )
if ! ok {
return nil , fmt . Errorf ( "cannot determine the device number for device %s" , path )
}
devNumber := int ( stat_t . Rdev )
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 ,
} , nil
}
func HostDevices ( ) ( [ ] * configs . Device , error ) {
return getDevices ( "/dev" )
}
func getDevices ( path string ) ( [ ] * configs . Device , error ) {
files , err := ioutilReadDir ( path )
if err != nil {
return nil , err
}
out := [ ] * configs . Device { }
for _ , f := range files {
switch {
case f . IsDir ( ) :
switch f . Name ( ) {
case "pts" , "shm" , "fd" , "mqueue" :
continue
default :
sub , err := getDevices ( filepath . Join ( path , f . Name ( ) ) )
if err != nil {
return nil , err
}
out = append ( out , sub ... )
continue
}
case f . Name ( ) == "console" :
continue
}
device , err := DeviceFromPath ( filepath . Join ( path , f . Name ( ) ) , "rwm" )
if err != nil {
if err == ErrNotADevice {
continue
}
2017-02-06 20:16:36 +00:00
if os . IsNotExist ( err ) {
continue
}
2017-02-01 00:45:59 +00:00
return nil , err
}
out = append ( out , device )
}
return out , nil
}