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 }