197 lines
4.6 KiB
Go
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
|
|
}
|