cmd/root, pkg/utils: Split out the code to acquire a file lock

A subsequent commit will use this to give Toolbx containers access to
the certificates from certificate authorities on the host.

This changes the user-visible error message from:
  $ toolbox --verbose list
  ...
  DEBU Migrating to newer Podman: failed to create migration lock file
      /run/user/1000/toolbox/migrate.lock: open
      /run/user/1000/toolbox/migrate.lock: no such file or directory
  Error: failed to create migration lock file

... to:
  $ toolbox --verbose list
  ...
  DEBU Migrating to newer Podman: failed to create lock file
      /run/user/1000/toolbox/migrate.lock: open
      /run/user/1000/toolbox/migrate.lock: no such file or directory
  Error: failed to create lock file

Or, from:
  $ toolbox --verbose list
  ...
  DEBU Migrating to newer Podman: failed to acquire migration lock on
      /run/user/1000/toolbox/migrate.lock: bad file descriptor
  Error: failed to acquire migration lock

... to:
  $ toolbox --verbose list
  ...
  DEBU Migrating to newer Podman: failed to acquire lock on
      /run/user/1000/toolbox/migrate.lock: bad file descriptor
  Error: failed to acquire lock

This is admittedly less specific without the debug logs, but it's
probably alright because it's such an unlikely error.

https://github.com/containers/toolbox/issues/626
This commit is contained in:
Debarshi Ray 2025-04-10 19:55:23 +02:00
parent b7be82d51e
commit 456f37794d
3 changed files with 64 additions and 11 deletions

View File

@ -263,21 +263,28 @@ func migrate(cmd *cobra.Command, args []string) error {
migrateLock := toolboxRuntimeDirectory + "/migrate.lock"
migrateLockFile, err := os.Create(migrateLock)
migrateLockFile, err := utils.Flock(migrateLock, syscall.LOCK_EX)
if err != nil {
logrus.Debugf("Migrating to newer Podman: failed to create migration lock file %s: %s", migrateLock, err)
return errors.New("failed to create migration lock file")
logrus.Debugf("Migrating to newer Podman: %s", err)
var errFlock *utils.FlockError
if errors.As(err, &errFlock) {
if errors.Is(err, utils.ErrFlockAcquire) {
err = utils.ErrFlockAcquire
} else if errors.Is(err, utils.ErrFlockCreate) {
err = utils.ErrFlockCreate
} else {
panicMsg := fmt.Sprintf("unexpected %T: %s", err, err)
panic(panicMsg)
}
}
return err
}
defer migrateLockFile.Close()
migrateLockFD := migrateLockFile.Fd()
migrateLockFDInt := int(migrateLockFD)
if err := syscall.Flock(migrateLockFDInt, syscall.LOCK_EX); err != nil {
logrus.Debugf("Migrating to newer Podman: failed to acquire migration lock on %s: %s", migrateLock, err)
return errors.New("failed to acquire migration lock")
}
stampBytes, err := ioutil.ReadFile(stampPath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2022 2024 Red Hat Inc.
* Copyright © 2022 2025 Red Hat Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,6 +31,12 @@ type DistroError struct {
Err error
}
type FlockError struct {
Path string
Errs []error
errSuffix string
}
type ImageError struct {
Image string
Err error
@ -58,6 +64,25 @@ func (err *DistroError) Unwrap() error {
return err.Err
}
func (err *FlockError) Error() string {
if err.Errs == nil || len(err.Errs) != 2 {
panicMsg := fmt.Sprintf("invalid %T", err)
panic(panicMsg)
}
errSuffix := " "
if err.errSuffix != "" {
errSuffix = fmt.Sprintf(" %s ", err.errSuffix)
}
errMsg := fmt.Sprintf("%s%s%s: %s", err.Errs[0], errSuffix, err.Path, err.Errs[1])
return errMsg
}
func (err *FlockError) Unwrap() []error {
return err.Errs
}
func (err *ImageError) Error() string {
errMsg := fmt.Sprintf("%s: %s", err.Image, err.Err)
return errMsg

View File

@ -164,6 +164,10 @@ var (
ErrDistroWithoutRelease = errors.New("non-default distribution must specify release")
ErrFlockAcquire = errors.New("failed to acquire lock")
ErrFlockCreate = errors.New("failed to create lock file")
ErrImageWithoutBasename = errors.New("image does not have a basename")
)
@ -227,6 +231,23 @@ func EnsureXdgRuntimeDirIsSet(uid int) {
}
}
func Flock(path string, how int) (*os.File, error) {
file, err := os.Create(path)
if err != nil {
errs := []error{ErrFlockCreate, err}
return nil, &FlockError{Path: path, Errs: errs}
}
fd := file.Fd()
fdInt := int(fd)
if err := syscall.Flock(fdInt, how); err != nil {
errs := []error{ErrFlockAcquire, err}
return nil, &FlockError{Path: path, Errs: errs, errSuffix: "on"}
}
return file, nil
}
func ForwardToHost() (int, error) {
envOptions := GetEnvOptionsForPreservedVariables()
toolboxPath := os.Getenv("TOOLBOX_PATH")