github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/util/mountOpts.go (about)

     1  package util
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/pkg/errors"
     7  )
     8  
     9  var (
    10  	// ErrBadMntOption indicates that an invalid mount option was passed.
    11  	ErrBadMntOption = errors.Errorf("invalid mount option")
    12  	// ErrDupeMntOption indicates that a duplicate mount option was passed.
    13  	ErrDupeMntOption = errors.Errorf("duplicate mount option passed")
    14  )
    15  
    16  type defaultMountOptions struct {
    17  	noexec bool
    18  	nosuid bool
    19  	nodev  bool
    20  }
    21  
    22  // ProcessOptions parses the options for a bind or tmpfs mount and ensures that
    23  // they are sensible and follow convention. The isTmpfs variable controls
    24  // whether extra, tmpfs-specific options will be allowed.
    25  // The sourcePath variable, if not empty, contains a bind mount source.
    26  func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
    27  	var (
    28  		foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ bool
    29  	)
    30  
    31  	var newOptions []string
    32  
    33  	for _, opt := range options {
    34  		// Some options have parameters - size, mode
    35  		splitOpt := strings.SplitN(opt, "=", 2)
    36  		switch splitOpt[0] {
    37  		case "exec", "noexec":
    38  			if foundExec {
    39  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used")
    40  			}
    41  			foundExec = true
    42  		case "suid", "nosuid":
    43  			if foundSuid {
    44  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nosuid' and 'suid' can be used")
    45  			}
    46  			foundSuid = true
    47  		case "nodev", "dev":
    48  			if foundDev {
    49  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nodev' and 'dev' can be used")
    50  			}
    51  			foundDev = true
    52  		case "rw", "ro":
    53  			if foundWrite {
    54  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used")
    55  			}
    56  			foundWrite = true
    57  		case "private", "rprivate", "slave", "rslave", "shared", "rshared":
    58  			if foundProp {
    59  				return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used")
    60  			}
    61  			foundProp = true
    62  		case "size":
    63  			if !isTmpfs {
    64  				return nil, errors.Wrapf(ErrBadMntOption, "the 'size' option is only allowed with tmpfs mounts")
    65  			}
    66  			if foundSize {
    67  				return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs size can be specified")
    68  			}
    69  			foundSize = true
    70  		case "mode":
    71  			if !isTmpfs {
    72  				return nil, errors.Wrapf(ErrBadMntOption, "the 'mode' option is only allowed with tmpfs mounts")
    73  			}
    74  			if foundMode {
    75  				return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs mode can be specified")
    76  			}
    77  			foundMode = true
    78  		case "tmpcopyup":
    79  			if !isTmpfs {
    80  				return nil, errors.Wrapf(ErrBadMntOption, "the 'tmpcopyup' option is only allowed with tmpfs mounts")
    81  			}
    82  			if foundCopyUp {
    83  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
    84  			}
    85  			foundCopyUp = true
    86  		case "notmpcopyup":
    87  			if !isTmpfs {
    88  				return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts")
    89  			}
    90  			if foundCopyUp {
    91  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
    92  			}
    93  			foundCopyUp = true
    94  			// do not propagate notmpcopyup to the OCI runtime
    95  			continue
    96  		case "bind", "rbind":
    97  			if isTmpfs {
    98  				return nil, errors.Wrapf(ErrBadMntOption, "the 'bind' and 'rbind' options are not allowed with tmpfs mounts")
    99  			}
   100  			if foundBind {
   101  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rbind' and 'bind' can be used")
   102  			}
   103  			foundBind = true
   104  		case "z", "Z":
   105  			if isTmpfs {
   106  				return nil, errors.Wrapf(ErrBadMntOption, "the 'z' and 'Z' options are not allowed with tmpfs mounts")
   107  			}
   108  			if foundZ {
   109  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'z' and 'Z' can be used")
   110  			}
   111  		default:
   112  			return nil, errors.Wrapf(ErrBadMntOption, "unknown mount option %q", opt)
   113  		}
   114  		newOptions = append(newOptions, opt)
   115  	}
   116  
   117  	if !foundWrite {
   118  		newOptions = append(newOptions, "rw")
   119  	}
   120  	if !foundProp {
   121  		newOptions = append(newOptions, "rprivate")
   122  	}
   123  	defaults, err := getDefaultMountOptions(sourcePath)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	if !foundExec && defaults.noexec {
   128  		newOptions = append(newOptions, "noexec")
   129  	}
   130  	if !foundSuid && defaults.nosuid {
   131  		newOptions = append(newOptions, "nosuid")
   132  	}
   133  	if !foundDev && defaults.nodev {
   134  		newOptions = append(newOptions, "nodev")
   135  	}
   136  	if isTmpfs && !foundCopyUp {
   137  		newOptions = append(newOptions, "tmpcopyup")
   138  	}
   139  	if !isTmpfs && !foundBind {
   140  		newOptions = append(newOptions, "rbind")
   141  	}
   142  
   143  	return newOptions, nil
   144  }