| 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, ",") |
| } |