github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/util/fs/mount/mount.go (about)

     1  // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package mount
     7  
     8  import (
     9  	"fmt"
    10  	"path/filepath"
    11  	"strings"
    12  	"syscall"
    13  
    14  	"github.com/sylabs/singularity/pkg/util/fs/proc"
    15  
    16  	specs "github.com/opencontainers/runtime-spec/specs-go"
    17  )
    18  
    19  type mountError string
    20  
    21  func (e mountError) Error() string { return string(e) }
    22  
    23  const (
    24  	// ErrMountExists indicates a duplicated mount being asked for
    25  	ErrMountExists = mountError("destination is already in the mount point list")
    26  )
    27  
    28  var mountFlags = []struct {
    29  	option string
    30  	flag   uintptr
    31  }{
    32  	{"acl", 0},
    33  	{"async", syscall.MS_ASYNC},
    34  	{"atime", 0},
    35  	{"bind", syscall.MS_BIND},
    36  	{"defaults", 0},
    37  	{"dev", 0},
    38  	{"diratime", 0},
    39  	{"dirsync", syscall.MS_DIRSYNC},
    40  	{"exec", 0},
    41  	{"iversion", 0},
    42  	{"lazytime", 0},
    43  	{"loud", 0},
    44  	{"mand", syscall.MS_MANDLOCK},
    45  	{"noacl", 0},
    46  	{"noatime", syscall.MS_NOATIME},
    47  	{"nodev", syscall.MS_NODEV},
    48  	{"nodiratime", syscall.MS_NODIRATIME},
    49  	{"noexec", syscall.MS_NOEXEC},
    50  	{"noiversion", 0},
    51  	{"nolazytime", 0},
    52  	{"nomand", 0},
    53  	{"norelatime", 0},
    54  	{"nostrictatime", 0},
    55  	{"nosuid", syscall.MS_NOSUID},
    56  	{"private", syscall.MS_PRIVATE},
    57  	{"rbind", syscall.MS_BIND | syscall.MS_REC},
    58  	{"rprivate", syscall.MS_PRIVATE | syscall.MS_REC},
    59  	{"rslave", syscall.MS_SLAVE | syscall.MS_REC},
    60  	{"rshared", syscall.MS_SHARED | syscall.MS_REC},
    61  	{"runbindable", syscall.MS_UNBINDABLE | syscall.MS_REC},
    62  	{"relatime", syscall.MS_RELATIME},
    63  	{"remount", syscall.MS_REMOUNT},
    64  	{"ro", syscall.MS_RDONLY},
    65  	{"rw", 0},
    66  	{"shared", syscall.MS_SHARED},
    67  	{"slave", syscall.MS_SLAVE},
    68  	{"silent", syscall.MS_SILENT},
    69  	{"strictatime", syscall.MS_STRICTATIME},
    70  	{"suid", 0},
    71  	{"sync", syscall.MS_SYNCHRONOUS},
    72  	{"unbindable", syscall.MS_UNBINDABLE},
    73  }
    74  
    75  type fsContext struct {
    76  	context bool
    77  }
    78  
    79  // AuthorizedTag defines the tag type
    80  type AuthorizedTag string
    81  
    82  const (
    83  	// SessionTag defines tag for session directory
    84  	SessionTag AuthorizedTag = "sessiondir"
    85  	// RootfsTag defines tag for container root filesystem
    86  	RootfsTag = "rootfs"
    87  	// PreLayerTag defines tag to prepare overlay/underlay layer
    88  	PreLayerTag = "prelayer"
    89  	// LayerTag defines tag for overlay/underlay final mount point
    90  	LayerTag = "layer"
    91  	// DevTag defines tag for dev related mount point
    92  	DevTag = "dev"
    93  	// HostfsTag defines tag for host filesystem mount point
    94  	HostfsTag = "hostfs"
    95  	// BindsTag defines tag for bind path
    96  	BindsTag = "binds"
    97  	// KernelTag defines tag for kernel related mount point (proc, sysfs)
    98  	KernelTag = "kernel"
    99  	// HomeTag defines tag for home directory mount point
   100  	HomeTag = "home"
   101  	// TmpTag defines tag for temporary filesystem mount points (/tmp, /var/tmp)
   102  	TmpTag = "tmp"
   103  	// ScratchTag defines tag for scratch mount points
   104  	ScratchTag = "scratch"
   105  	// CwdTag defines tag for current working directory mount point
   106  	CwdTag = "cwd"
   107  	// FilesTag defines tag for file mount points (passwd, group ...)
   108  	FilesTag = "files"
   109  	// UserbindsTag defines tag for user bind mount points
   110  	UserbindsTag = "userbinds"
   111  	// OtherTag defines tag for other mount points that can't be classified
   112  	OtherTag = "other"
   113  	// FinalTag defines tag for mount points to mount/remount at the end of mount process
   114  	FinalTag = "final"
   115  )
   116  
   117  var authorizedTags = map[AuthorizedTag]struct {
   118  	multiPoint bool
   119  	order      int
   120  }{
   121  	SessionTag:   {false, 0},
   122  	RootfsTag:    {false, 1},
   123  	PreLayerTag:  {true, 2},
   124  	LayerTag:     {false, 3},
   125  	DevTag:       {true, 4},
   126  	HostfsTag:    {true, 5},
   127  	BindsTag:     {true, 6},
   128  	KernelTag:    {true, 7},
   129  	HomeTag:      {false, 8},
   130  	TmpTag:       {true, 9},
   131  	ScratchTag:   {true, 10},
   132  	CwdTag:       {false, 11},
   133  	FilesTag:     {true, 12},
   134  	UserbindsTag: {true, 13},
   135  	OtherTag:     {true, 14},
   136  	FinalTag:     {true, 15},
   137  }
   138  
   139  var authorizedImage = map[string]fsContext{
   140  	"ext3":     {true},
   141  	"squashfs": {true},
   142  }
   143  
   144  var authorizedFS = map[string]fsContext{
   145  	"overlay": {true},
   146  	"tmpfs":   {true},
   147  	"ramfs":   {true},
   148  	"devpts":  {true},
   149  	"sysfs":   {false},
   150  	"proc":    {false},
   151  	"mqueue":  {false},
   152  	"cgroup":  {false},
   153  }
   154  
   155  var internalOptions = []string{"loop", "offset", "sizelimit"}
   156  
   157  // Point describes a mount point
   158  type Point struct {
   159  	specs.Mount
   160  	InternalOptions []string `json:"internalOptions"`
   161  }
   162  
   163  // Points defines and stores a set of mount points by tag
   164  type Points struct {
   165  	context string
   166  	points  map[AuthorizedTag][]Point
   167  }
   168  
   169  // ConvertOptions converts an options string into a pair of mount flags and mount options
   170  func ConvertOptions(options []string) (uintptr, []string) {
   171  	var flags uintptr
   172  	finalOpt := []string{}
   173  	isFlag := false
   174  
   175  	for _, option := range options {
   176  		optionTrim := strings.TrimSpace(option)
   177  		for _, flag := range mountFlags {
   178  			if flag.option == optionTrim {
   179  				flags |= flag.flag
   180  				isFlag = true
   181  				break
   182  			}
   183  		}
   184  		if !isFlag {
   185  			finalOpt = append(finalOpt, optionTrim)
   186  		}
   187  		isFlag = false
   188  	}
   189  	return flags, finalOpt
   190  }
   191  
   192  // ConvertSpec converts an OCI Mount spec into an importable mount points list
   193  func ConvertSpec(mounts []specs.Mount) (map[AuthorizedTag][]Point, error) {
   194  	var tag AuthorizedTag
   195  
   196  	points := make(map[AuthorizedTag][]Point)
   197  	for _, m := range mounts {
   198  		var options []string
   199  		var propagationOption string
   200  		var err error
   201  		source := m.Source
   202  		mountType := m.Type
   203  
   204  		tag = ""
   205  		if mountType != "" && mountType != "bind" && mountType != "none" {
   206  			if _, ok := authorizedFS[mountType]; !ok {
   207  				return points, fmt.Errorf("%s filesystem type is not authorized", mountType)
   208  			}
   209  			if has, err := proc.HasFilesystem(mountType); err != nil || !has {
   210  				return points, fmt.Errorf("%s filesystem not supported", mountType)
   211  			}
   212  			tag = KernelTag
   213  		} else {
   214  			source, err = filepath.Abs(m.Source)
   215  			if err != nil {
   216  				return points, fmt.Errorf("failed to determine absolute path for %s: %s", m.Source, err)
   217  			}
   218  			tag = UserbindsTag
   219  			mountType = ""
   220  		}
   221  
   222  		for _, opt := range m.Options {
   223  			switch opt {
   224  			case "shared",
   225  				"rshared",
   226  				"slave",
   227  				"rslave",
   228  				"private",
   229  				"rprivate",
   230  				"unbindable",
   231  				"runbindable":
   232  				propagationOption = opt
   233  			default:
   234  				options = append(options, opt)
   235  			}
   236  		}
   237  
   238  		points[tag] = append(points[tag], Point{
   239  			Mount: specs.Mount{
   240  				Source:      source,
   241  				Destination: m.Destination,
   242  				Type:        mountType,
   243  				Options:     options,
   244  			},
   245  		})
   246  
   247  		if len(options) > 1 && tag == UserbindsTag {
   248  			options = append(options, "remount")
   249  			points[tag] = append(points[tag], Point{
   250  				Mount: specs.Mount{
   251  					Source:      "",
   252  					Destination: m.Destination,
   253  					Type:        "",
   254  					Options:     options,
   255  				},
   256  			})
   257  		}
   258  		if propagationOption != "" {
   259  			points[tag] = append(points[tag], Point{
   260  				Mount: specs.Mount{
   261  					Source:      "",
   262  					Destination: m.Destination,
   263  					Type:        "",
   264  					Options:     []string{propagationOption},
   265  				},
   266  			})
   267  		}
   268  	}
   269  	return points, nil
   270  }
   271  
   272  // GetTagList returns authorized tags in right order
   273  func GetTagList() []AuthorizedTag {
   274  	tagList := make([]AuthorizedTag, len(authorizedTags))
   275  	for k, tag := range authorizedTags {
   276  		tagList[tag.order] = k
   277  	}
   278  	return tagList
   279  }
   280  
   281  // GetOffset return offset value for image options
   282  func GetOffset(options []string) (uint64, error) {
   283  	var offset uint64
   284  	for _, opt := range options {
   285  		if strings.HasPrefix(opt, "offset=") {
   286  			fmt.Sscanf(opt, "offset=%d", &offset)
   287  			return offset, nil
   288  		}
   289  	}
   290  	return 0, fmt.Errorf("offset option not found")
   291  }
   292  
   293  // GetSizeLimit returns sizelimit value for image options
   294  func GetSizeLimit(options []string) (uint64, error) {
   295  	var size uint64
   296  	for _, opt := range options {
   297  		if strings.HasPrefix(opt, "sizelimit=") {
   298  			fmt.Sscanf(opt, "sizelimit=%d", &size)
   299  			return size, nil
   300  		}
   301  	}
   302  	return 0, fmt.Errorf("sizelimit option not found")
   303  }
   304  
   305  // HasRemountFlag checks if remount flag is set or not.
   306  func HasRemountFlag(flags uintptr) bool {
   307  	return flags&syscall.MS_REMOUNT != 0
   308  }
   309  
   310  // HasPropagationFlag checks if a propagation flag is set or not.
   311  func HasPropagationFlag(flags uintptr) bool {
   312  	return flags&getPropagationFlags() != 0
   313  }
   314  
   315  func getPropagationFlags() uintptr {
   316  	return syscall.MS_UNBINDABLE | syscall.MS_SHARED | syscall.MS_PRIVATE | syscall.MS_SLAVE
   317  }
   318  
   319  func (p *Points) init() {
   320  	if p.points == nil {
   321  		p.points = make(map[AuthorizedTag][]Point)
   322  	}
   323  }
   324  
   325  func (p *Points) add(tag AuthorizedTag, source string, dest string, fstype string, flags uintptr, options string) error {
   326  	var bind = false
   327  
   328  	p.init()
   329  
   330  	mountOpts := []string{}
   331  	internalOpts := []string{}
   332  
   333  	if dest == "" {
   334  		return fmt.Errorf("mount point must contain a destination")
   335  	}
   336  	if !strings.HasPrefix(dest, "/") {
   337  		return fmt.Errorf("destination must be an absolute path")
   338  	}
   339  	if _, ok := authorizedTags[tag]; !ok {
   340  		return fmt.Errorf("tag %s is not a recognized tag", tag)
   341  	}
   342  	if !HasRemountFlag(flags) && !HasPropagationFlag(flags) {
   343  		present := false
   344  		for _, point := range p.points[tag] {
   345  			if point.Destination == dest {
   346  				present = true
   347  				break
   348  			}
   349  		}
   350  		if present {
   351  			return ErrMountExists
   352  		}
   353  
   354  		if len(p.points[tag]) == 1 && !authorizedTags[tag].multiPoint {
   355  			return fmt.Errorf("tag %s allow only one mount point", tag)
   356  		}
   357  	}
   358  	for i := len(mountFlags) - 1; i >= 0; i-- {
   359  		flag := mountFlags[i].flag
   360  		if flag != 0 && flag == (flags&flag) {
   361  			if bind && flag&syscall.MS_BIND != 0 {
   362  				continue
   363  			}
   364  			mountOpts = append(mountOpts, mountFlags[i].option)
   365  			if flag&syscall.MS_BIND != 0 {
   366  				bind = true
   367  			}
   368  		}
   369  	}
   370  	setContext := true
   371  	for _, option := range strings.Split(options, ",") {
   372  		o := strings.TrimSpace(option)
   373  		if o != "" {
   374  			keyVal := strings.SplitN(o, "=", 2)
   375  			if keyVal[0] == "context" {
   376  				setContext = false
   377  			}
   378  			isInternal := false
   379  			for _, internal := range internalOptions {
   380  				if keyVal[0] == internal {
   381  					isInternal = true
   382  				}
   383  			}
   384  			if isInternal == true {
   385  				internalOpts = append(internalOpts, o)
   386  			} else {
   387  				mountOpts = append(mountOpts, o)
   388  			}
   389  		}
   390  	}
   391  	if fstype != "" && setContext {
   392  		setContext = authorizedFS[fstype].context
   393  	}
   394  	if !bind && setContext && p.context != "" {
   395  		context := fmt.Sprintf("context=%q", p.context)
   396  		mountOpts = append(mountOpts, context)
   397  	}
   398  	p.points[tag] = append(p.points[tag], Point{
   399  		Mount: specs.Mount{
   400  			Source:      source,
   401  			Destination: dest,
   402  			Type:        fstype,
   403  			Options:     mountOpts,
   404  		},
   405  		InternalOptions: internalOpts,
   406  	})
   407  	return nil
   408  }
   409  
   410  // GetAll returns all registered mount points
   411  func (p *Points) GetAll() map[AuthorizedTag][]Point {
   412  	p.init()
   413  	return p.points
   414  }
   415  
   416  // GetByDest returns registered mount points with the matched destination
   417  func (p *Points) GetByDest(dest string) []Point {
   418  	p.init()
   419  	mounts := []Point{}
   420  	for tag := range p.points {
   421  		for _, point := range p.points[tag] {
   422  			if point.Destination == dest {
   423  				mounts = append(mounts, point)
   424  			}
   425  		}
   426  	}
   427  	return mounts
   428  }
   429  
   430  // GetBySource returns registered mount points with the matched source
   431  func (p *Points) GetBySource(source string) []Point {
   432  	p.init()
   433  	mounts := []Point{}
   434  	for tag := range p.points {
   435  		for _, point := range p.points[tag] {
   436  			if point.Source == source {
   437  				mounts = append(mounts, point)
   438  			}
   439  		}
   440  	}
   441  	return mounts
   442  }
   443  
   444  // GetByTag returns mount points attached to a tag
   445  func (p *Points) GetByTag(tag AuthorizedTag) []Point {
   446  	p.init()
   447  	return p.points[tag]
   448  }
   449  
   450  // RemoveAll removes all mounts points from list
   451  func (p *Points) RemoveAll() {
   452  	p.init()
   453  	for tag := range p.points {
   454  		p.points[tag] = nil
   455  	}
   456  }
   457  
   458  // RemoveByDest removes mount points identified by destination
   459  func (p *Points) RemoveByDest(dest string) {
   460  	p.init()
   461  	for tag := range p.points {
   462  		for d := len(p.points[tag]) - 1; d >= 0; d-- {
   463  			if p.points[tag][d].Destination == dest {
   464  				p.points[tag] = append(p.points[tag][:d], p.points[tag][d+1:]...)
   465  			}
   466  		}
   467  	}
   468  }
   469  
   470  // RemoveBySource removes mount points identified by source
   471  func (p *Points) RemoveBySource(source string) {
   472  	p.init()
   473  	for tag := range p.points {
   474  		for d := len(p.points[tag]) - 1; d >= 0; d-- {
   475  			if p.points[tag][d].Source == source {
   476  				p.points[tag] = append(p.points[tag][:d], p.points[tag][d+1:]...)
   477  			}
   478  		}
   479  	}
   480  }
   481  
   482  // RemoveByTag removes mount points attached to a tag
   483  func (p *Points) RemoveByTag(tag AuthorizedTag) {
   484  	p.init()
   485  	p.points[tag] = nil
   486  }
   487  
   488  // Import imports a mount point list
   489  func (p *Points) Import(points map[AuthorizedTag][]Point) error {
   490  	for tag := range points {
   491  		for _, point := range points[tag] {
   492  			var err error
   493  			var offset uint64
   494  			var sizelimit uint64
   495  
   496  			flags, options := ConvertOptions(point.Options)
   497  			// check if this is a mount point to remount
   498  			if HasRemountFlag(flags) {
   499  				if err = p.AddRemount(tag, point.Destination, flags); err == nil {
   500  					continue
   501  				}
   502  			}
   503  			if HasPropagationFlag(flags) {
   504  				if err = p.AddPropagation(tag, point.Destination, flags); err == nil {
   505  					continue
   506  				}
   507  			}
   508  			// check if this is a bind mount point
   509  			if flags&syscall.MS_BIND != 0 {
   510  				if err = p.AddBind(tag, point.Source, point.Destination, flags); err == nil {
   511  					continue
   512  				} else {
   513  					return err
   514  				}
   515  			}
   516  
   517  			for _, option := range point.InternalOptions {
   518  				if strings.HasPrefix(option, "offset=") {
   519  					fmt.Sscanf(option, "offset=%d", &offset)
   520  				}
   521  				if strings.HasPrefix(option, "sizelimit=") {
   522  					fmt.Sscanf(option, "sizelimit=%d", &sizelimit)
   523  				}
   524  			}
   525  
   526  			// check if this is an image mount point
   527  			if err = p.AddImage(tag, point.Source, point.Destination, point.Type, flags, offset, sizelimit); err == nil {
   528  				continue
   529  			}
   530  
   531  			// check if this is a filesystem or overlay mount point
   532  			if point.Type != "overlay" {
   533  				if err = p.AddFSWithSource(tag, point.Source, point.Destination, point.Type, flags, strings.Join(options, ",")); err == nil {
   534  					continue
   535  				}
   536  			} else {
   537  				lowerdir := ""
   538  				upperdir := ""
   539  				workdir := ""
   540  				for _, option := range options {
   541  					if strings.HasPrefix(option, "lowerdir=") {
   542  						fmt.Sscanf(option, "lowerdir=%s", &lowerdir)
   543  					} else if strings.HasPrefix(option, "upperdir=") {
   544  						fmt.Sscanf(option, "upperdir=%s", &upperdir)
   545  					} else if strings.HasPrefix(option, "workdir=") {
   546  						fmt.Sscanf(option, "workdir=%s", &workdir)
   547  					}
   548  				}
   549  				if err = p.AddOverlay(tag, point.Destination, flags, lowerdir, upperdir, workdir); err == nil {
   550  					continue
   551  				}
   552  			}
   553  			p.RemoveAll()
   554  			return fmt.Errorf("can't import mount points list: %s", err)
   555  		}
   556  	}
   557  	return nil
   558  }
   559  
   560  // ImportFromSpec converts an OCI Mount spec into a mount point list
   561  // and imports it
   562  func (p *Points) ImportFromSpec(mounts []specs.Mount) error {
   563  	points, err := ConvertSpec(mounts)
   564  	if err != nil {
   565  		return err
   566  	}
   567  	return p.Import(points)
   568  }
   569  
   570  // AddImage adds an image mount point
   571  func (p *Points) AddImage(tag AuthorizedTag, source string, dest string, fstype string, flags uintptr, offset uint64, sizelimit uint64) error {
   572  	options := ""
   573  	if source == "" {
   574  		return fmt.Errorf("an image mount point must contain a source")
   575  	}
   576  	if !strings.HasPrefix(source, "/") {
   577  		return fmt.Errorf("source must be an absolute path")
   578  	}
   579  	if flags&(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_REC) != 0 {
   580  		return fmt.Errorf("MS_BIND, MS_REC or MS_REMOUNT are not valid flags for image mount points")
   581  	}
   582  	if _, ok := authorizedImage[fstype]; !ok {
   583  		return fmt.Errorf("mount %s image is not authorized", fstype)
   584  	}
   585  	if sizelimit == 0 {
   586  		return fmt.Errorf("invalid image size, zero length")
   587  	}
   588  	options = fmt.Sprintf("loop,offset=%d,sizelimit=%d,errors=remount-ro", offset, sizelimit)
   589  	return p.add(tag, source, dest, fstype, flags, options)
   590  }
   591  
   592  // GetAllImages returns a list of all registered image mount points
   593  func (p *Points) GetAllImages() []Point {
   594  	p.init()
   595  	images := []Point{}
   596  	for tag := range p.points {
   597  		for _, point := range p.points[tag] {
   598  			if _, ok := authorizedImage[point.Type]; ok {
   599  				images = append(images, point)
   600  			}
   601  		}
   602  	}
   603  	return images
   604  }
   605  
   606  // AddBind adds a bind mount point
   607  func (p *Points) AddBind(tag AuthorizedTag, source string, dest string, flags uintptr) error {
   608  	bindFlags := flags | syscall.MS_BIND
   609  	options := ""
   610  
   611  	if source == "" {
   612  		return fmt.Errorf("a bind mount point must contain a source")
   613  	}
   614  	if !strings.HasPrefix(source, "/") {
   615  		return fmt.Errorf("source must be an absolute path")
   616  	}
   617  	return p.add(tag, source, dest, "", bindFlags, options)
   618  }
   619  
   620  // GetAllBinds returns a list of all registered bind mount points
   621  func (p *Points) GetAllBinds() []Point {
   622  	p.init()
   623  	binds := []Point{}
   624  	for tag := range p.points {
   625  		for _, point := range p.points[tag] {
   626  			for _, option := range point.Options {
   627  				if option == "bind" || option == "rbind" {
   628  					binds = append(binds, point)
   629  					break
   630  				}
   631  			}
   632  		}
   633  	}
   634  	return binds
   635  }
   636  
   637  // AddOverlay adds an overlay mount point
   638  func (p *Points) AddOverlay(tag AuthorizedTag, dest string, flags uintptr, lowerdir string, upperdir string, workdir string) error {
   639  	if flags&(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_REC) != 0 {
   640  		return fmt.Errorf("MS_BIND, MS_REC or MS_REMOUNT are not valid flags for overlay mount points")
   641  	}
   642  	if lowerdir == "" {
   643  		return fmt.Errorf("overlay mount point %s should have at least lowerdir option", dest)
   644  	}
   645  	if !strings.HasPrefix(lowerdir, "/") {
   646  		return fmt.Errorf("lowerdir may contain only an absolute paths")
   647  	}
   648  	options := ""
   649  	if upperdir != "" {
   650  		if !strings.HasPrefix(upperdir, "/") {
   651  			return fmt.Errorf("upperdir must be an absolute path")
   652  		}
   653  		if workdir == "" {
   654  			return fmt.Errorf("overlay mount point %s should have workdir option set when used in conjunction with upperdir", dest)
   655  		}
   656  		if !strings.HasPrefix(workdir, "/") {
   657  			return fmt.Errorf("workdir must be an absolute path")
   658  		}
   659  		options = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerdir, upperdir, workdir)
   660  	} else {
   661  		options = fmt.Sprintf("lowerdir=%s", lowerdir)
   662  	}
   663  	return p.add(tag, "overlay", dest, "overlay", flags, options)
   664  }
   665  
   666  // GetAllOverlays returns a list of all registered overlay mount points
   667  func (p *Points) GetAllOverlays() []Point {
   668  	p.init()
   669  	fs := []Point{}
   670  	for tag := range p.points {
   671  		for _, point := range p.points[tag] {
   672  			if point.Type == "overlay" {
   673  				fs = append(fs, point)
   674  			}
   675  		}
   676  	}
   677  	return fs
   678  }
   679  
   680  // AddFS adds a filesystem mount point
   681  func (p *Points) AddFS(tag AuthorizedTag, dest string, fstype string, flags uintptr, options string) error {
   682  	return p.AddFSWithSource(tag, fstype, dest, fstype, flags, options)
   683  }
   684  
   685  // AddFSWithSource adds a filesystem mount point
   686  func (p *Points) AddFSWithSource(tag AuthorizedTag, source string, dest string, fstype string, flags uintptr, options string) error {
   687  	if flags&(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_REC) != 0 {
   688  		return fmt.Errorf("MS_BIND, MS_REC or MS_REMOUNT are not valid flags for FS mount points")
   689  	}
   690  	if _, ok := authorizedFS[fstype]; !ok {
   691  		return fmt.Errorf("mount %s file system is not authorized", fstype)
   692  	}
   693  	return p.add(tag, source, dest, fstype, flags, options)
   694  }
   695  
   696  // GetAllFS returns a list of all registered filesystem mount points
   697  func (p *Points) GetAllFS() []Point {
   698  	p.init()
   699  	fs := []Point{}
   700  	for tag := range p.points {
   701  		for _, point := range p.points[tag] {
   702  			for fstype := range authorizedFS {
   703  				if fstype == point.Type && point.Type != "overlay" {
   704  					fs = append(fs, point)
   705  				}
   706  			}
   707  		}
   708  	}
   709  	return fs
   710  }
   711  
   712  // AddRemount adds a mount point to remount
   713  func (p *Points) AddRemount(tag AuthorizedTag, dest string, flags uintptr) error {
   714  	remountFlags := (flags &^ getPropagationFlags()) | syscall.MS_REMOUNT
   715  	return p.add(tag, "", dest, "", remountFlags, "")
   716  }
   717  
   718  // AddPropagation adds a mount propagation for mount point
   719  func (p *Points) AddPropagation(tag AuthorizedTag, dest string, flags uintptr) error {
   720  	finalFlags := flags & getPropagationFlags()
   721  	if !HasPropagationFlag(finalFlags) {
   722  		return fmt.Errorf("no mount propagation flag found")
   723  	}
   724  	if flags&syscall.MS_REC != 0 {
   725  		finalFlags |= syscall.MS_REC
   726  	}
   727  	return p.add(tag, "", dest, "", finalFlags, "")
   728  }
   729  
   730  // SetContext sets SELinux mount context, once set it can't be modified
   731  func (p *Points) SetContext(context string) error {
   732  	if p.context == "" {
   733  		p.context = context
   734  		return nil
   735  	}
   736  	return fmt.Errorf("mount context has already been set")
   737  }
   738  
   739  // GetContext returns SELinux mount context
   740  func (p *Points) GetContext() string {
   741  	return p.context
   742  }