github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/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, foundU, foundOverlay, foundIdmap bool
    29  	)
    30  
    31  	newOptions := make([]string, 0, len(options))
    32  	for _, opt := range options {
    33  		// Some options have parameters - size, mode
    34  		splitOpt := strings.SplitN(opt, "=", 2)
    35  
    36  		// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
    37  		if foundOverlay {
    38  			if strings.Contains(opt, "upperdir") {
    39  				newOptions = append(newOptions, opt)
    40  				continue
    41  			}
    42  			if strings.Contains(opt, "workdir") {
    43  				newOptions = append(newOptions, opt)
    44  				continue
    45  			}
    46  		}
    47  
    48  		if strings.HasPrefix(splitOpt[0], "idmap") {
    49  			if foundIdmap {
    50  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'idmap' option can only be set once")
    51  			}
    52  			foundIdmap = true
    53  			newOptions = append(newOptions, opt)
    54  			continue
    55  		}
    56  
    57  		switch splitOpt[0] {
    58  		case "O":
    59  			foundOverlay = true
    60  		case "volume-opt":
    61  			// Volume-opt should be relayed and processed by driver.
    62  			newOptions = append(newOptions, opt)
    63  		case "exec", "noexec":
    64  			if foundExec {
    65  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used")
    66  			}
    67  			foundExec = true
    68  		case "suid", "nosuid":
    69  			if foundSuid {
    70  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nosuid' and 'suid' can be used")
    71  			}
    72  			foundSuid = true
    73  		case "nodev", "dev":
    74  			if foundDev {
    75  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nodev' and 'dev' can be used")
    76  			}
    77  			foundDev = true
    78  		case "rw", "ro":
    79  			if foundWrite {
    80  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used")
    81  			}
    82  			foundWrite = true
    83  		case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable":
    84  			if foundProp {
    85  				return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used")
    86  			}
    87  			foundProp = true
    88  		case "size":
    89  			if !isTmpfs {
    90  				return nil, errors.Wrapf(ErrBadMntOption, "the 'size' option is only allowed with tmpfs mounts")
    91  			}
    92  			if foundSize {
    93  				return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs size can be specified")
    94  			}
    95  			foundSize = true
    96  		case "mode":
    97  			if !isTmpfs {
    98  				return nil, errors.Wrapf(ErrBadMntOption, "the 'mode' option is only allowed with tmpfs mounts")
    99  			}
   100  			if foundMode {
   101  				return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs mode can be specified")
   102  			}
   103  			foundMode = true
   104  		case "tmpcopyup":
   105  			if !isTmpfs {
   106  				return nil, errors.Wrapf(ErrBadMntOption, "the 'tmpcopyup' option is only allowed with tmpfs mounts")
   107  			}
   108  			if foundCopyUp {
   109  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
   110  			}
   111  			foundCopyUp = true
   112  		case "consistency":
   113  			// Often used on MACs and mistakenly on Linux platforms.
   114  			// Since Docker ignores this option so shall we.
   115  			continue
   116  		case "notmpcopyup":
   117  			if !isTmpfs {
   118  				return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts")
   119  			}
   120  			if foundCopyUp {
   121  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
   122  			}
   123  			foundCopyUp = true
   124  			// do not propagate notmpcopyup to the OCI runtime
   125  			continue
   126  		case "bind", "rbind":
   127  			if isTmpfs {
   128  				return nil, errors.Wrapf(ErrBadMntOption, "the 'bind' and 'rbind' options are not allowed with tmpfs mounts")
   129  			}
   130  			if foundBind {
   131  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rbind' and 'bind' can be used")
   132  			}
   133  			foundBind = true
   134  		case "z", "Z":
   135  			if isTmpfs {
   136  				return nil, errors.Wrapf(ErrBadMntOption, "the 'z' and 'Z' options are not allowed with tmpfs mounts")
   137  			}
   138  			if foundZ {
   139  				return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'z' and 'Z' can be used")
   140  			}
   141  			foundZ = true
   142  		case "U":
   143  			if foundU {
   144  				return nil, errors.Wrapf(ErrDupeMntOption, "the 'U' option can only be set once")
   145  			}
   146  			foundU = true
   147  		default:
   148  			return nil, errors.Wrapf(ErrBadMntOption, "unknown mount option %q", opt)
   149  		}
   150  		newOptions = append(newOptions, opt)
   151  	}
   152  
   153  	if !foundWrite {
   154  		newOptions = append(newOptions, "rw")
   155  	}
   156  	if !foundProp {
   157  		newOptions = append(newOptions, "rprivate")
   158  	}
   159  	defaults, err := getDefaultMountOptions(sourcePath)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	if !foundExec && defaults.noexec {
   164  		newOptions = append(newOptions, "noexec")
   165  	}
   166  	if !foundSuid && defaults.nosuid {
   167  		newOptions = append(newOptions, "nosuid")
   168  	}
   169  	if !foundDev && defaults.nodev {
   170  		newOptions = append(newOptions, "nodev")
   171  	}
   172  	if isTmpfs && !foundCopyUp {
   173  		newOptions = append(newOptions, "tmpcopyup")
   174  	}
   175  	if !isTmpfs && !foundBind {
   176  		newOptions = append(newOptions, "rbind")
   177  	}
   178  
   179  	return newOptions, nil
   180  }
   181  
   182  func ParseDriverOpts(option string) (string, string, error) {
   183  	token := strings.SplitN(option, "=", 2)
   184  	if len(token) != 2 {
   185  		return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts")
   186  	}
   187  	opt := strings.SplitN(token[1], "=", 2)
   188  	if len(opt) != 2 {
   189  		return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts")
   190  	}
   191  	return opt[0], opt[1], nil
   192  }