diff --git a/check.go b/check.go index 9170dcd7f..396648e7f 100644 --- a/check.go +++ b/check.go @@ -1003,12 +1003,12 @@ func (c *checkDirectory) remove(path string) { func (c *checkDirectory) header(hdr *tar.Header) { name := path.Clean(hdr.Name) dir, base := path.Split(name) - if strings.HasPrefix(base, archive.WhiteoutPrefix) { + if file, ok := strings.CutPrefix(base, archive.WhiteoutPrefix); ok { if base == archive.WhiteoutOpaqueDir { c.remove(path.Clean(dir)) c.add(path.Clean(dir), tar.TypeDir, hdr.Uid, hdr.Gid, hdr.Size, os.FileMode(hdr.Mode), hdr.ModTime.Unix()) } else { - c.remove(path.Join(dir, base[len(archive.WhiteoutPrefix):])) + c.remove(path.Join(dir, file)) } } else { if hdr.Typeflag == tar.TypeLink { diff --git a/cmd/containers-storage/copy.go b/cmd/containers-storage/copy.go index 9531f4625..9b973bc8e 100644 --- a/cmd/containers-storage/copy.go +++ b/cmd/containers-storage/copy.go @@ -22,36 +22,36 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st return 1, nil } if len(chownOptions) > 0 { - chownParts := strings.SplitN(chownOptions, ":", 2) - if len(chownParts) == 1 { - chownParts = append(chownParts, chownParts[0]) + uidStr, gidStr, ok := strings.Cut(chownOptions, ":") + if !ok { + gidStr = uidStr } - uid, err := strconv.ParseUint(chownParts[0], 10, 32) + uid, err := strconv.ParseUint(uidStr, 10, 32) if err != nil { - return 1, fmt.Errorf("error %q as a numeric UID: %v", chownParts[0], err) + return 1, fmt.Errorf("bad UID: %w", err) } - gid, err := strconv.ParseUint(chownParts[1], 10, 32) + gid, err := strconv.ParseUint(gidStr, 10, 32) if err != nil { - return 1, fmt.Errorf("error %q as a numeric GID: %v", chownParts[1], err) + return 1, fmt.Errorf("bad GID: %w", err) } chownOpts = &idtools.IDPair{UID: int(uid), GID: int(gid)} } target := args[len(args)-1] if strings.Contains(target, ":") { - targetParts := strings.SplitN(target, ":", 2) - if len(targetParts) != 2 { + layer, targetRel, ok := strings.Cut(target, ":") + if !ok { return 1, fmt.Errorf("error parsing target location %q: only one part", target) } - targetLayer, err := m.Layer(targetParts[0]) + targetLayer, err := m.Layer(layer) if err != nil { - return 1, fmt.Errorf("error finding layer %q: %+v", targetParts[0], err) + return 1, fmt.Errorf("error finding layer %q: %+v", layer, err) } untarIDMappings = idtools.NewIDMappingsFromMaps(targetLayer.UIDMap, targetLayer.GIDMap) targetMount, err := m.Mount(targetLayer.ID, targetLayer.MountLabel) if err != nil { return 1, fmt.Errorf("error mounting layer %q: %+v", targetLayer.ID, err) } - target = filepath.Join(targetMount, targetParts[1]) + target = filepath.Join(targetMount, targetRel) defer func() { if _, err := m.Unmount(targetLayer.ID, false); err != nil { fmt.Fprintf(os.Stderr, "error unmounting layer %q: %+v\n", targetLayer.ID, err) @@ -63,21 +63,17 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st for _, srcSpec := range args { var tarIDMappings *idtools.IDMappings source := srcSpec - if strings.Contains(source, ":") { - sourceParts := strings.SplitN(source, ":", 2) - if len(sourceParts) != 2 { - return 1, fmt.Errorf("error parsing source location %q: only one part", source) - } - sourceLayer, err := m.Layer(sourceParts[0]) + if layer, sourceRel, ok := strings.Cut(source, ":"); ok { + sourceLayer, err := m.Layer(layer) if err != nil { - return 1, fmt.Errorf("error finding layer %q: %+v", sourceParts[0], err) + return 1, fmt.Errorf("error finding layer %q: %+v", layer, err) } tarIDMappings = idtools.NewIDMappingsFromMaps(sourceLayer.UIDMap, sourceLayer.GIDMap) sourceMount, err := m.Mount(sourceLayer.ID, sourceLayer.MountLabel) if err != nil { return 1, fmt.Errorf("error mounting layer %q: %+v", sourceLayer.ID, err) } - source = filepath.Join(sourceMount, sourceParts[1]) + source = filepath.Join(sourceMount, sourceRel) defer func() { if _, err := m.Unmount(sourceLayer.ID, false); err != nil { fmt.Fprintf(os.Stderr, "error unmounting layer %q: %+v\n", sourceLayer.ID, err) diff --git a/drivers/overlay/mount.go b/drivers/overlay/mount.go index 8829e55e9..82c1a460b 100644 --- a/drivers/overlay/mount.go +++ b/drivers/overlay/mount.go @@ -103,20 +103,20 @@ func mountOverlayFromMain() { // paths, but we don't want to mess with other options. var upperk, upperv, workk, workv, lowerk, lowerv, labelk, labelv, others string for _, arg := range strings.Split(options.Label, ",") { - kv := strings.SplitN(arg, "=", 2) - switch kv[0] { + key, val, _ := strings.Cut(arg, "=") + switch key { case "upperdir": upperk = "upperdir=" - upperv = kv[1] + upperv = val case "workdir": workk = "workdir=" - workv = kv[1] + workv = val case "lowerdir": lowerk = "lowerdir=" - lowerv = kv[1] + lowerv = val case "label": labelk = "label=" - labelv = kv[1] + labelv = val default: if others == "" { others = arg diff --git a/drivers/windows/windows.go b/drivers/windows/windows.go index 18f90fdc5..d38e74534 100644 --- a/drivers/windows/windows.go +++ b/drivers/windows/windows.go @@ -764,8 +764,8 @@ func writeLayerFromTar(r io.Reader, w hcsshim.LayerWriter, root string) (int64, buf := bufio.NewWriter(nil) for err == nil { base := path.Base(hdr.Name) - if strings.HasPrefix(base, archive.WhiteoutPrefix) { - name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):]) + if rm, ok := strings.CutPrefix(base, archive.WhiteoutPrefix); ok { + name := path.Join(path.Dir(hdr.Name), rm) err = w.Remove(filepath.FromSlash(name)) if err != nil { return 0, err diff --git a/internal/opts/opts.go b/internal/opts/opts.go index 0a03395f1..d2b70d5db 100644 --- a/internal/opts/opts.go +++ b/internal/opts/opts.go @@ -144,12 +144,8 @@ func (opts *MapOpts) Set(value string) error { } value = v } - vals := strings.SplitN(value, "=", 2) - if len(vals) == 1 { - (opts.values)[vals[0]] = "" - } else { - (opts.values)[vals[0]] = vals[1] - } + key, val, _ := strings.Cut(value, "=") + opts.values[key] = val return nil } diff --git a/internal/opts/parse.go b/internal/opts/parse.go index be2a766ea..ebd8469f3 100644 --- a/internal/opts/parse.go +++ b/internal/opts/parse.go @@ -46,14 +46,13 @@ func ParseFlag(arg string, prev Args) (Args, error) { return filters, nil } - if !strings.Contains(arg, "=") { + name, value, ok := strings.Cut(arg, "=") + if !ok { return filters, ErrBadFormat } - f := strings.SplitN(arg, "=", 2) - - name := strings.ToLower(strings.TrimSpace(f[0])) - value := strings.TrimSpace(f[1]) + name = strings.ToLower(strings.TrimSpace(name)) + value = strings.TrimSpace(value) filters.Add(name, value) @@ -140,14 +139,14 @@ func (args Args) MatchKVList(key string, sources map[string]string) bool { return false } - for value := range fieldValues { - testKV := strings.SplitN(value, "=", 2) + for field := range fieldValues { + key, val, gotVal := strings.Cut(field, "=") - v, ok := sources[testKV[0]] + v, ok := sources[key] if !ok { return false } - if len(testKV) == 2 && testKV[1] != v { + if gotVal && val != v { return false } } diff --git a/pkg/archive/archive_linux.go b/pkg/archive/archive_linux.go index eae60a305..b9d718b60 100644 --- a/pkg/archive/archive_linux.go +++ b/pkg/archive/archive_linux.go @@ -124,8 +124,7 @@ func (overlayWhiteoutConverter) ConvertReadWithHandler(hdr *tar.Header, path str } // if a file was deleted and we are using overlay, we need to create a character device - if strings.HasPrefix(base, WhiteoutPrefix) { - originalBase := base[len(WhiteoutPrefix):] + if originalBase, ok := strings.CutPrefix(base, WhiteoutPrefix); ok { originalPath := filepath.Join(dir, originalBase) if err := handler.Mknod(originalPath, unix.S_IFCHR, 0); err != nil { diff --git a/pkg/archive/changes.go b/pkg/archive/changes.go index ba77454b3..3075c27bb 100644 --- a/pkg/archive/changes.go +++ b/pkg/archive/changes.go @@ -98,8 +98,7 @@ func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) { f := filepath.Base(path) // If there is a whiteout, then the file was removed - if strings.HasPrefix(f, WhiteoutPrefix) { - originalFile := f[len(WhiteoutPrefix):] + if originalFile, ok := strings.CutPrefix(f, WhiteoutPrefix); ok { return filepath.Join(filepath.Dir(path), originalFile), nil } diff --git a/pkg/archive/fflags_bsd.go b/pkg/archive/fflags_bsd.go index 92b8d05ed..5b8dc84e2 100644 --- a/pkg/archive/fflags_bsd.go +++ b/pkg/archive/fflags_bsd.go @@ -80,9 +80,9 @@ func parseFileFlags(fflags string) (uint32, uint32, error) { var set, clear uint32 = 0, 0 for _, fflag := range strings.Split(fflags, ",") { isClear := false - if strings.HasPrefix(fflag, "no") { + if clean, ok := strings.CutPrefix(fflag, "no"); ok { isClear = true - fflag = strings.TrimPrefix(fflag, "no") + fflag = clean } if value, ok := flagNameToValue[fflag]; ok { if isClear { diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go index 074ed980e..a4ac13e00 100644 --- a/pkg/mflag/flag.go +++ b/pkg/mflag/flag.go @@ -554,8 +554,10 @@ func (fs *FlagSet) PrintDefaults() { if len(names) > 0 && len(flag.Usage) > 0 { val := flag.DefValue - if home != "" && strings.HasPrefix(val, home) { - val = homedir.GetShortcutString() + val[len(home):] + if home != "" { + if relhome, ok := strings.CutPrefix(val, home); ok { + val = homedir.GetShortcutString() + relhome + } } if isZeroValue(val) { diff --git a/pkg/mount/flags.go b/pkg/mount/flags.go index 5de3a671d..40a229932 100644 --- a/pkg/mount/flags.go +++ b/pkg/mount/flags.go @@ -97,14 +97,14 @@ func MergeTmpfsOptions(options []string) ([]string, error) { } continue } - opt := strings.SplitN(option, "=", 2) - if len(opt) != 2 || !validFlags[opt[0]] { + opt, _, ok := strings.Cut(option, "=") + if !ok || !validFlags[opt] { return nil, fmt.Errorf("invalid tmpfs option %q", opt) } - if !dataCollisions[opt[0]] { + if !dataCollisions[opt] { // We prepend the option and add to collision map newOptions = append([]string{option}, newOptions...) - dataCollisions[opt[0]] = true + dataCollisions[opt] = true } } @@ -140,8 +140,8 @@ func ParseOptions(options string) (int, string) { func ParseTmpfsOptions(options string) (int, string, error) { flags, data := ParseOptions(options) for _, o := range strings.Split(data, ",") { - opt := strings.SplitN(o, "=", 2) - if !validFlags[opt[0]] { + opt, _, _ := strings.Cut(o, "=") + if !validFlags[opt] { return 0, "", fmt.Errorf("invalid tmpfs option %q", opt) } } diff --git a/pkg/mount/mounter_freebsd.go b/pkg/mount/mounter_freebsd.go index c70b0bf99..afd321041 100644 --- a/pkg/mount/mounter_freebsd.go +++ b/pkg/mount/mounter_freebsd.go @@ -40,13 +40,9 @@ func mount(device, target, mType string, flag uintptr, data string) error { isNullFS = true continue } - opt := strings.SplitN(x, "=", 2) - options = append(options, opt[0]) - if len(opt) == 2 { - options = append(options, opt[1]) - } else { - options = append(options, "") - } + name, val, _ := strings.Cut(x, "=") + options = append(options, name) + options = append(options, val) } } diff --git a/pkg/mount/mounter_linux_test.go b/pkg/mount/mounter_linux_test.go index bf8d713dc..24936943f 100644 --- a/pkg/mount/mounter_linux_test.go +++ b/pkg/mount/mounter_linux_test.go @@ -209,7 +209,8 @@ func validateMount(t *testing.T, mnt string, opts, optional, vfs string) { // clean strips off any value param after the colon func clean(v string) string { - return strings.SplitN(v, ":", 2)[0] + ret, _, _ := strings.Cut(v, ":") + return ret } // has returns true if key is a member of m diff --git a/pkg/parsers/kernel/kernel_darwin.go b/pkg/parsers/kernel/kernel_darwin.go index 645790da6..675a14055 100644 --- a/pkg/parsers/kernel/kernel_darwin.go +++ b/pkg/parsers/kernel/kernel_darwin.go @@ -36,12 +36,12 @@ func getRelease() (string, error) { for _, line := range data { if strings.Contains(line, "Kernel Version") { // It has the format like ' Kernel Version: Darwin 14.5.0' - content := strings.SplitN(line, ":", 2) - if len(content) != 2 { + _, val, ok := strings.Cut(line, ":") + if !ok { return "", fmt.Errorf("kernel version is invalid") } - prettyNames, err := shellwords.Parse(content[1]) + prettyNames, err := shellwords.Parse(val) if err != nil { return "", fmt.Errorf("kernel version is invalid: %w", err) } diff --git a/pkg/parsers/operatingsystem/operatingsystem_linux.go b/pkg/parsers/operatingsystem/operatingsystem_linux.go index de071bb9a..6275e37cf 100644 --- a/pkg/parsers/operatingsystem/operatingsystem_linux.go +++ b/pkg/parsers/operatingsystem/operatingsystem_linux.go @@ -41,14 +41,13 @@ func GetOperatingSystem() (string, error) { scanner := bufio.NewScanner(osReleaseFile) for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, "PRETTY_NAME=") { - data := strings.SplitN(line, "=", 2) - prettyNames, err := shellwords.Parse(data[1]) + if name, ok := strings.CutPrefix(line, "PRETTY_NAME="); ok { + prettyNames, err := shellwords.Parse(name) if err != nil { return "", fmt.Errorf("PRETTY_NAME is invalid: %s", err.Error()) } if len(prettyNames) != 1 { - return "", fmt.Errorf("PRETTY_NAME needs to be enclosed by quotes if they have spaces: %s", data[1]) + return "", fmt.Errorf("PRETTY_NAME needs to be enclosed by quotes if they have spaces: %s", name) } prettyName = prettyNames[0] } diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go index 3fb0c36b8..7b20b0628 100644 --- a/pkg/parsers/parsers.go +++ b/pkg/parsers/parsers.go @@ -11,11 +11,11 @@ import ( // ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value) func ParseKeyValueOpt(opt string) (string, string, error) { - parts := strings.SplitN(opt, "=", 2) - if len(parts) != 2 { + k, v, ok := strings.Cut(opt, "=") + if !ok { return "", "", fmt.Errorf("unable to parse key/value option: %s", opt) } - return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil + return strings.TrimSpace(k), strings.TrimSpace(v), nil } // ParseUintList parses and validates the specified string as the value @@ -42,19 +42,19 @@ func ParseUintList(val string) (map[int]bool, error) { errInvalidFormat := fmt.Errorf("invalid format: %s", val) for _, r := range split { - if !strings.Contains(r, "-") { + minS, maxS, ok := strings.Cut(r, "-") + if !ok { v, err := strconv.Atoi(r) if err != nil { return nil, errInvalidFormat } availableInts[v] = true } else { - split := strings.SplitN(r, "-", 2) - min, err := strconv.Atoi(split[0]) + min, err := strconv.Atoi(minS) if err != nil { return nil, errInvalidFormat } - max, err := strconv.Atoi(split[1]) + max, err := strconv.Atoi(maxS) if err != nil { return nil, errInvalidFormat } diff --git a/types/options.go b/types/options.go index f1a900b8d..efc08c476 100644 --- a/types/options.go +++ b/types/options.go @@ -344,8 +344,8 @@ func getRootlessStorageOpts(systemOpts StoreOptions) (StoreOptions, error) { dirEntries, err := os.ReadDir(opts.GraphRoot) if err == nil { for _, entry := range dirEntries { - if strings.HasSuffix(entry.Name(), "-images") { - opts.GraphDriverName = strings.TrimSuffix(entry.Name(), "-images") + if name, ok := strings.CutSuffix(entry.Name(), "-images"); ok { + opts.GraphDriverName = name break } }