2014-02-18 07:14:30 +08:00
package devices
import (
2014-05-31 09:30:27 +08:00
"errors"
"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
2015-06-22 10:29:59 +08:00
"github.com/opencontainers/runc/libcontainer/configs"
2017-05-10 05:38:27 +08:00
"golang.org/x/sys/unix"
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 (
2017-07-21 22:54:47 +08:00
unixLstat = unix . Lstat
2014-09-05 08:00:09 +08:00
ioutilReadDir = ioutil . ReadDir
2014-09-05 07:34:34 +08:00
)
2016-12-01 16:48:09 +08: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.
2015-02-01 11:56:27 +08:00
func DeviceFromPath ( path , permissions string ) ( * configs . Device , error ) {
2017-07-21 22:54:47 +08:00
var stat unix . Stat_t
err := unixLstat ( path , & stat )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
}
2017-08-09 23:47:57 +08:00
var (
2018-06-17 09:22:01 +08:00
devNumber = uint64 ( stat . Rdev )
2017-10-17 14:30:01 +08:00
major = unix . Major ( devNumber )
2018-06-17 09:22:01 +08:00
minor = unix . Minor ( devNumber )
2017-08-09 23:47:57 +08:00
)
if major == 0 {
return nil , ErrNotADevice
}
2014-02-18 07:14:30 +08:00
var (
2017-07-21 22:54:47 +08:00
devType rune
mode = stat . Mode
2014-02-18 07:14:30 +08:00
)
switch {
2017-08-04 02:06:54 +08:00
case mode & unix . S_IFBLK == unix . S_IFBLK :
2017-07-21 22:54:47 +08:00
devType = 'b'
2017-08-04 02:06:54 +08:00
case mode & unix . S_IFCHR == unix . S_IFCHR :
2014-02-18 07:14:30 +08:00
devType = 'c'
}
2015-02-01 11:56:27 +08:00
return & configs . Device {
Type : devType ,
Path : path ,
2017-10-17 14:30:01 +08:00
Major : int64 ( major ) ,
2018-06-17 09:22:01 +08:00
Minor : int64 ( minor ) ,
2015-02-01 11:56:27 +08:00
Permissions : permissions ,
2017-07-21 22:54:47 +08:00
FileMode : os . FileMode ( mode ) ,
2017-08-09 23:47:57 +08:00
Uid : stat . Uid ,
Gid : stat . 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 ( ) {
2017-03-08 23:25:10 +08:00
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
2017-02-15 02:27:26 +08:00
case "pts" , "shm" , "fd" , "mqueue" , ".lxc" , ".lxd-mounts" :
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
}
2016-12-08 03:08:00 +08:00
if os . IsNotExist ( err ) {
continue
}
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
}