2019-09-30 10:53:51 -04:00

197 lines
4.6 KiB
Go

// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
// isulad-tools is licensed under the Mulan PSL v1.
// You can use this software according to the terms and conditions of the Mulan PSL v1.
// You may obtain a copy of Mulan PSL v1 at:
// http://license.coscl.org.cn/MulanPSL
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v1 for more details.
// Description: bind operation for device
// Author: zhangwei
// Create: 2018-01-18
package libdevice
import (
"bufio"
"fmt"
"os"
"os/exec"
"path"
"strings"
"github.com/sirupsen/logrus"
"github.com/opencontainers/runtime-spec/specs-go"
"isula.org/isulad-tools/types"
"isula.org/isulad-tools/utils"
)
// ParseBind will parse host path to Bind structure
func ParseBind(bindstr string, spec *specs.Spec, create bool) (*types.Bind, error) {
var src, dst string
var isDir bool
permissions := "rw,rslave"
arr := strings.Split(bindstr, ":")
switch len(arr) {
case 3:
if validMountOption(arr[2]) == false {
return nil, fmt.Errorf("invalid permissions: %s", arr[2])
}
permissions = arr[2]
fallthrough
case 2:
src = path.Clean(arr[0])
dst = path.Clean(arr[1])
default:
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
}
if path.IsAbs(src) == false || path.IsAbs(dst) == false {
return nil, fmt.Errorf("invalid path specification:%s, only absolute path is allowed", bindstr)
}
bind := &types.Bind{
HostPath: src,
ContainerPath: dst,
MountOption: permissions,
}
symlinkinfo, err := os.Lstat(src)
if err != nil {
logrus.Errorf("Lstat returns a FileInfo describing the named file error: %v", err)
}
info, err := os.Stat(src)
if symlinkinfo != nil {
if ((symlinkinfo.Mode() & os.ModeSymlink) == os.ModeSymlink) && (err != nil) {
return nil, fmt.Errorf("Parsebind get symlibk source file error: %v", err)
}
}
if create {
if spec != nil {
uid, gid := utils.GetUIDGid(spec)
if uid == -1 {
uid = 0
}
if gid == -1 {
gid = 0
}
bind.UID = uid
bind.GID = gid
}
if err == nil {
isDir = info.IsDir()
} else if os.IsNotExist(err) {
isDir = true
if spec != nil {
if err := os.MkdirAll(src, os.FileMode(0755)); err != nil {
return nil, fmt.Errorf("ParseBind mkdir error: %v", err)
}
if err := os.Chown(src, bind.UID, bind.GID); err != nil {
return nil, fmt.Errorf("ParseBind chown error: %v", err)
}
}
} else {
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
}
} else {
if err == nil {
isDir = info.IsDir()
} else if os.IsNotExist(err) {
return bind, nil
} else {
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
}
}
bind.IsDir = isDir
return bind, nil
}
func findPathDevice(path string) (*types.Device, string, error) {
// find path mount entry point.
cmd := exec.Command("df", "-P", path)
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, "", err
}
if err := cmd.Start(); err != nil {
return nil, "", err
}
defer cmd.Wait()
reader := bufio.NewReader(stdout)
// ignore first line.
reader.ReadString('\n')
line, err := reader.ReadString('\n')
if err != nil {
logrus.Errorf("reader.ReadString error: %v", err)
}
line = strings.Trim(line, "\n")
devs := strings.Split(line, " ")
device, err := DeviceFromPath(devs[0], "rwm")
if err != nil {
return nil, "", err
}
return device, devs[len(devs)-1], nil
}
func findDeviceMountEntryPoint(device, mp string) (string, string, string, error) {
f, err := os.Open("/proc/mounts")
if err != nil {
return "", "", "", err
}
defer f.Close()
scanner := bufio.NewScanner(f)
procMntCols := 6
for scanner.Scan() {
line := scanner.Text()
array := strings.Split(line, " ")
if len(array) < procMntCols {
continue
}
// the one we wanted.
if array[0] == device && array[1] == mp {
entry := array[1]
fstype := array[2]
mountOption := array[3]
return entry, fstype, mountOption, nil
}
}
if err := scanner.Err(); err != nil {
return "", "", "", err
}
return "", "", "", fmt.Errorf("Device Not Found")
}
// validMountOption will validate the mount option for user input
func validMountOption(option string) bool {
validOp := map[string]bool{
"ro": true,
"rw": true,
// "shared": true,
"private": true,
// "slave": true,
// "rshared": true,
"rprivate": true,
"rslave": true,
}
arr := strings.Split(option, ",")
for _, op := range arr {
if !validOp[op] {
return false
}
validOp[op] = false
}
return true
}