158 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|    Copyright The containerd Authors.
 | |
| 
 | |
|    Licensed under the Apache License, Version 2.0 (the "License");
 | |
|    you may not use this file except in compliance with the License.
 | |
|    You may obtain a copy of the License at
 | |
| 
 | |
|        http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
|    Unless required by applicable law or agreed to in writing, software
 | |
|    distributed under the License is distributed on an "AS IS" BASIS,
 | |
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|    See the License for the specific language governing permissions and
 | |
|    limitations under the License.
 | |
| */
 | |
| 
 | |
| package mount
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/pkg/errors"
 | |
| 	"golang.org/x/sys/unix"
 | |
| )
 | |
| 
 | |
| // Mount to the provided target path
 | |
| func (m *Mount) Mount(target string) error {
 | |
| 	flags, data := parseMountOptions(m.Options)
 | |
| 
 | |
| 	// propagation types.
 | |
| 	const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
 | |
| 
 | |
| 	// Ensure propagation type change flags aren't included in other calls.
 | |
| 	oflags := flags &^ ptypes
 | |
| 
 | |
| 	// In the case of remounting with changed data (data != ""), need to call mount (moby/moby#34077).
 | |
| 	if flags&unix.MS_REMOUNT == 0 || data != "" {
 | |
| 		// Initial call applying all non-propagation flags for mount
 | |
| 		// or remount with changed data
 | |
| 		if err := unix.Mount(m.Source, target, m.Type, uintptr(oflags), data); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if flags&ptypes != 0 {
 | |
| 		// Change the propagation type.
 | |
| 		const pflags = ptypes | unix.MS_REC | unix.MS_SILENT
 | |
| 		if err := unix.Mount("", target, "", uintptr(flags&pflags), ""); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	const broflags = unix.MS_BIND | unix.MS_RDONLY
 | |
| 	if oflags&broflags == broflags {
 | |
| 		// Remount the bind to apply read only.
 | |
| 		return unix.Mount("", target, "", uintptr(oflags|unix.MS_REMOUNT), "")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Unmount the provided mount path with the flags
 | |
| func Unmount(target string, flags int) error {
 | |
| 	if err := unmount(target, flags); err != nil && err != unix.EINVAL {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func unmount(target string, flags int) error {
 | |
| 	for i := 0; i < 50; i++ {
 | |
| 		if err := unix.Unmount(target, flags); err != nil {
 | |
| 			switch err {
 | |
| 			case unix.EBUSY:
 | |
| 				time.Sleep(50 * time.Millisecond)
 | |
| 				continue
 | |
| 			default:
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	return errors.Wrapf(unix.EBUSY, "failed to unmount target %s", target)
 | |
| }
 | |
| 
 | |
| // UnmountAll repeatedly unmounts the given mount point until there
 | |
| // are no mounts remaining (EINVAL is returned by mount), which is
 | |
| // useful for undoing a stack of mounts on the same mount point.
 | |
| func UnmountAll(mount string, flags int) error {
 | |
| 	for {
 | |
| 		if err := unmount(mount, flags); err != nil {
 | |
| 			// EINVAL is returned if the target is not a
 | |
| 			// mount point, indicating that we are
 | |
| 			// done. It can also indicate a few other
 | |
| 			// things (such as invalid flags) which we
 | |
| 			// unfortunately end up squelching here too.
 | |
| 			if err == unix.EINVAL {
 | |
| 				return nil
 | |
| 			}
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // parseMountOptions takes fstab style mount options and parses them for
 | |
| // use with a standard mount() syscall
 | |
| func parseMountOptions(options []string) (int, string) {
 | |
| 	var (
 | |
| 		flag int
 | |
| 		data []string
 | |
| 	)
 | |
| 	flags := map[string]struct {
 | |
| 		clear bool
 | |
| 		flag  int
 | |
| 	}{
 | |
| 		"async":         {true, unix.MS_SYNCHRONOUS},
 | |
| 		"atime":         {true, unix.MS_NOATIME},
 | |
| 		"bind":          {false, unix.MS_BIND},
 | |
| 		"defaults":      {false, 0},
 | |
| 		"dev":           {true, unix.MS_NODEV},
 | |
| 		"diratime":      {true, unix.MS_NODIRATIME},
 | |
| 		"dirsync":       {false, unix.MS_DIRSYNC},
 | |
| 		"exec":          {true, unix.MS_NOEXEC},
 | |
| 		"mand":          {false, unix.MS_MANDLOCK},
 | |
| 		"noatime":       {false, unix.MS_NOATIME},
 | |
| 		"nodev":         {false, unix.MS_NODEV},
 | |
| 		"nodiratime":    {false, unix.MS_NODIRATIME},
 | |
| 		"noexec":        {false, unix.MS_NOEXEC},
 | |
| 		"nomand":        {true, unix.MS_MANDLOCK},
 | |
| 		"norelatime":    {true, unix.MS_RELATIME},
 | |
| 		"nostrictatime": {true, unix.MS_STRICTATIME},
 | |
| 		"nosuid":        {false, unix.MS_NOSUID},
 | |
| 		"rbind":         {false, unix.MS_BIND | unix.MS_REC},
 | |
| 		"relatime":      {false, unix.MS_RELATIME},
 | |
| 		"remount":       {false, unix.MS_REMOUNT},
 | |
| 		"ro":            {false, unix.MS_RDONLY},
 | |
| 		"rw":            {true, unix.MS_RDONLY},
 | |
| 		"strictatime":   {false, unix.MS_STRICTATIME},
 | |
| 		"suid":          {true, unix.MS_NOSUID},
 | |
| 		"sync":          {false, unix.MS_SYNCHRONOUS},
 | |
| 	}
 | |
| 	for _, o := range options {
 | |
| 		// If the option does not exist in the flags table or the flag
 | |
| 		// is not supported on the platform,
 | |
| 		// then it is a data value for a specific fs type
 | |
| 		if f, exists := flags[o]; exists && f.flag != 0 {
 | |
| 			if f.clear {
 | |
| 				flag &^= f.flag
 | |
| 			} else {
 | |
| 				flag |= f.flag
 | |
| 			}
 | |
| 		} else {
 | |
| 			data = append(data, o)
 | |
| 		}
 | |
| 	}
 | |
| 	return flag, strings.Join(data, ",")
 | |
| }
 | 
