package mount

import (
	"syscall"
)

const (
	// ptypes is the set propagation types.
	ptypes = syscall.MS_SHARED | syscall.MS_PRIVATE | syscall.MS_SLAVE | syscall.MS_UNBINDABLE

	// pflags is the full set valid flags for a change propagation call.
	pflags = ptypes | syscall.MS_REC | syscall.MS_SILENT

	// broflags is the combination of bind and read only
	broflags = syscall.MS_BIND | syscall.MS_RDONLY
)

// isremount returns true if either device name or flags identify a remount request, false otherwise.
func isremount(device string, flags uintptr) bool {
	switch {
	// We treat device "" and "none" as a remount request to provide compatibility with
	// requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts.
	case flags&syscall.MS_REMOUNT != 0, device == "", device == "none":
		return true
	default:
		return false
	}
}

func mount(device, target, mType string, flags uintptr, data string) error {
	oflags := flags &^ ptypes
	if !isremount(device, flags) {
		// Initial call applying all non-propagation flags.
		if err := syscall.Mount(device, target, mType, oflags, data); err != nil {
			return err
		}
	}

	if flags&ptypes != 0 {
		// Change the propagation type.
		if err := syscall.Mount("", target, "", flags&pflags, ""); err != nil {
			return err
		}
	}

	if oflags&broflags == broflags {
		// Remount the bind to apply read only.
		return syscall.Mount("", target, "", oflags|syscall.MS_REMOUNT, "")
	}

	return nil
}

func unmount(target string, flag int) error {
	return syscall.Unmount(target, flag)
}