mirror of
https://github.com/containers/podman.git
synced 2025-12-08 23:00:23 +08:00
Update common, image, and storage deps
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
243
vendor/github.com/containers/storage/internal/tempdir/tempdir.go
generated
vendored
Normal file
243
vendor/github.com/containers/storage/internal/tempdir/tempdir.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package tempdir
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/storage/internal/staging_lockfile"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
/*
|
||||
Locking rules and invariants for TempDir and its recovery mechanism:
|
||||
|
||||
1. TempDir Instance Locks:
|
||||
- Path: 'RootDir/lock-XYZ' (in the root directory)
|
||||
- Each TempDir instance creates and holds an exclusive lock on this file immediately
|
||||
during NewTempDir() initialization.
|
||||
- This lock signifies that the temporary directory is in active use by the
|
||||
process/goroutine that holds the TempDir object.
|
||||
|
||||
2. Stale Directory Recovery (separate operation):
|
||||
- RecoverStaleDirs() can be called independently to identify and clean up stale
|
||||
temporary directories.
|
||||
- For each potential stale directory (found by listPotentialStaleDirs), it
|
||||
attempts to TryLockPath() its instance lock file.
|
||||
- If TryLockPath() succeeds: The directory is considered stale, and both the
|
||||
directory and lock file are removed.
|
||||
- If TryLockPath() fails: The directory is considered in active use by another
|
||||
process/goroutine, and it's skipped.
|
||||
|
||||
3. TempDir Usage:
|
||||
- NewTempDir() immediately creates both the instance lock and the temporary directory.
|
||||
- TempDir.StageDeletion() moves files into the existing temporary directory with counter-based naming.
|
||||
- Files moved into the temporary directory are renamed with a counter-based prefix
|
||||
to ensure uniqueness (e.g., "0-filename", "1-filename").
|
||||
- Once cleaned up, the TempDir instance cannot be reused - StageDeletion() will return an error.
|
||||
|
||||
4. Cleanup Process:
|
||||
- TempDir.Cleanup() removes both the temporary directory and its lock file.
|
||||
- The instance lock is unlocked and deleted after cleanup operations are complete.
|
||||
- The TempDir instance becomes inactive after cleanup (internal fields are reset).
|
||||
- The TempDir instance cannot be reused after Cleanup() - StageDeletion() will fail.
|
||||
|
||||
5. TempDir Lifetime:
|
||||
- NewTempDir() creates both the TempDir manager and the actual temporary directory immediately.
|
||||
- The temporary directory is created eagerly during NewTempDir().
|
||||
- During its lifetime, the temporary directory is protected by its instance lock.
|
||||
- The temporary directory exists until Cleanup() is called, which removes both
|
||||
the directory and its lock file.
|
||||
- Multiple TempDir instances can coexist in the same RootDir, each with its own
|
||||
unique subdirectory and lock.
|
||||
- After cleanup, the TempDir instance cannot be reused.
|
||||
|
||||
6. Example Directory Structure:
|
||||
|
||||
RootDir/
|
||||
lock-ABC (instance lock for temp-dir-ABC)
|
||||
temp-dir-ABC/
|
||||
0-file1
|
||||
1-file3
|
||||
lock-XYZ (instance lock for temp-dir-XYZ)
|
||||
temp-dir-XYZ/
|
||||
0-file2
|
||||
*/
|
||||
const (
|
||||
// tempDirPrefix is the prefix used for creating temporary directories.
|
||||
tempDirPrefix = "temp-dir-"
|
||||
// tempdirLockPrefix is the prefix used for creating lock files for temporary directories.
|
||||
tempdirLockPrefix = "lock-"
|
||||
)
|
||||
|
||||
// TempDir represents a temporary directory that is created in a specified root directory.
|
||||
// It manages the lifecycle of the temporary directory, including creation, locking, and cleanup.
|
||||
// Each TempDir instance is associated with a unique subdirectory in the root directory.
|
||||
// Warning: The TempDir instance should be used in a single goroutine.
|
||||
type TempDir struct {
|
||||
RootDir string
|
||||
|
||||
tempDirPath string
|
||||
// tempDirLock is a lock file (e.g., RootDir/lock-XYZ) specific to this
|
||||
// TempDir instance, indicating it's in active use.
|
||||
tempDirLock *staging_lockfile.StagingLockFile
|
||||
tempDirLockPath string
|
||||
|
||||
// counter is used to generate unique filenames for added files.
|
||||
counter uint64
|
||||
}
|
||||
|
||||
// CleanupTempDirFunc is a function type that can be returned by operations
|
||||
// which need to perform cleanup actions later.
|
||||
type CleanupTempDirFunc func() error
|
||||
|
||||
// listPotentialStaleDirs scans the RootDir for directories that might be stale temporary directories.
|
||||
// It identifies directories with the tempDirPrefix and their corresponding lock files with the tempdirLockPrefix.
|
||||
// The function returns a map of IDs that correspond to both directories and lock files found.
|
||||
// These IDs are extracted from the filenames by removing their respective prefixes.
|
||||
func listPotentialStaleDirs(rootDir string) (map[string]struct{}, error) {
|
||||
ids := make(map[string]struct{})
|
||||
|
||||
dirContent, err := os.ReadDir(rootDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error reading temp dir %s: %w", rootDir, err)
|
||||
}
|
||||
|
||||
for _, entry := range dirContent {
|
||||
if id, ok := strings.CutPrefix(entry.Name(), tempDirPrefix); ok {
|
||||
ids[id] = struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
if id, ok := strings.CutPrefix(entry.Name(), tempdirLockPrefix); ok {
|
||||
ids[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// RecoverStaleDirs identifies and removes stale temporary directories in the root directory.
|
||||
// A directory is considered stale if its lock file can be acquired (indicating no active use).
|
||||
// The function attempts to remove both the directory and its lock file.
|
||||
// If a directory's lock cannot be acquired, it is considered in use and is skipped.
|
||||
func RecoverStaleDirs(rootDir string) error {
|
||||
potentialStaleDirs, err := listPotentialStaleDirs(rootDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing potential stale temp dirs in %s: %w", rootDir, err)
|
||||
}
|
||||
|
||||
if len(potentialStaleDirs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var recoveryErrors []error
|
||||
|
||||
for id := range potentialStaleDirs {
|
||||
lockPath := filepath.Join(rootDir, tempdirLockPrefix+id)
|
||||
tempDirPath := filepath.Join(rootDir, tempDirPrefix+id)
|
||||
|
||||
// Try to lock the lock file. If it can be locked, the directory is stale.
|
||||
instanceLock, err := staging_lockfile.TryLockPath(lockPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if rmErr := os.RemoveAll(tempDirPath); rmErr != nil && !os.IsNotExist(rmErr) {
|
||||
recoveryErrors = append(recoveryErrors, fmt.Errorf("error removing stale temp dir %s: %w", tempDirPath, rmErr))
|
||||
}
|
||||
if unlockErr := instanceLock.UnlockAndDelete(); unlockErr != nil {
|
||||
recoveryErrors = append(recoveryErrors, fmt.Errorf("error unlocking and deleting stale lock file %s: %w", lockPath, unlockErr))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Join(recoveryErrors...)
|
||||
}
|
||||
|
||||
// NewTempDir creates a TempDir and immediately creates both the temporary directory
|
||||
// and its corresponding lock file in the specified RootDir.
|
||||
// The RootDir itself will be created if it doesn't exist.
|
||||
// Note: The caller MUST ensure that returned TempDir instance is cleaned up with .Cleanup().
|
||||
func NewTempDir(rootDir string) (*TempDir, error) {
|
||||
if err := os.MkdirAll(rootDir, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("creating root temp directory %s failed: %w", rootDir, err)
|
||||
}
|
||||
|
||||
td := &TempDir{
|
||||
RootDir: rootDir,
|
||||
}
|
||||
tempDirLock, tempDirLockFileName, err := staging_lockfile.CreateAndLock(td.RootDir, tempdirLockPrefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating and locking temp dir instance lock in %s failed: %w", td.RootDir, err)
|
||||
}
|
||||
td.tempDirLock = tempDirLock
|
||||
td.tempDirLockPath = filepath.Join(td.RootDir, tempDirLockFileName)
|
||||
|
||||
// Create the temporary directory that corresponds to the lock file
|
||||
id := strings.TrimPrefix(tempDirLockFileName, tempdirLockPrefix)
|
||||
actualTempDirPath := filepath.Join(td.RootDir, tempDirPrefix+id)
|
||||
if err := os.MkdirAll(actualTempDirPath, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("creating temp directory %s failed: %w", actualTempDirPath, err)
|
||||
}
|
||||
td.tempDirPath = actualTempDirPath
|
||||
td.counter = 0
|
||||
return td, nil
|
||||
}
|
||||
|
||||
// StageDeletion moves the specified file into the instance's temporary directory.
|
||||
// The temporary directory must already exist (created during NewTempDir).
|
||||
// Files are renamed with a counter-based prefix (e.g., "0-filename", "1-filename") to ensure uniqueness.
|
||||
// Note: 'path' must be on the same filesystem as the TempDir for os.Rename to work.
|
||||
// The caller MUST ensure .Cleanup() is called.
|
||||
// If the TempDir has been cleaned up, this method will return an error.
|
||||
func (td *TempDir) StageDeletion(path string) error {
|
||||
if td.tempDirLock == nil {
|
||||
return fmt.Errorf("temp dir instance not initialized or already cleaned up")
|
||||
}
|
||||
fileName := fmt.Sprintf("%d-", td.counter) + filepath.Base(path)
|
||||
destPath := filepath.Join(td.tempDirPath, fileName)
|
||||
td.counter++
|
||||
return os.Rename(path, destPath)
|
||||
}
|
||||
|
||||
// Cleanup removes the temporary directory and releases its instance lock.
|
||||
// After cleanup, the TempDir instance becomes inactive and cannot be reused.
|
||||
// Subsequent calls to StageDeletion() will fail.
|
||||
// Multiple calls to Cleanup() are safe and will not return an error.
|
||||
// Callers should typically defer Cleanup() to run after any application-level
|
||||
// global locks are released to avoid holding those locks during potentially
|
||||
// slow disk I/O.
|
||||
func (td *TempDir) Cleanup() error {
|
||||
if td.tempDirLock == nil {
|
||||
logrus.Debug("Temp dir already cleaned up")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(td.tempDirPath); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("removing temp dir %s failed: %w", td.tempDirPath, err)
|
||||
}
|
||||
|
||||
lock := td.tempDirLock
|
||||
td.tempDirPath = ""
|
||||
td.tempDirLock = nil
|
||||
td.tempDirLockPath = ""
|
||||
return lock.UnlockAndDelete()
|
||||
}
|
||||
|
||||
// CleanupTemporaryDirectories cleans up multiple temporary directories by calling their cleanup functions.
|
||||
func CleanupTemporaryDirectories(cleanFuncs ...CleanupTempDirFunc) error {
|
||||
var cleanupErrors []error
|
||||
for _, cleanupFunc := range cleanFuncs {
|
||||
if cleanupFunc == nil {
|
||||
continue
|
||||
}
|
||||
if err := cleanupFunc(); err != nil {
|
||||
cleanupErrors = append(cleanupErrors, err)
|
||||
}
|
||||
}
|
||||
return errors.Join(cleanupErrors...)
|
||||
}
|
||||
Reference in New Issue
Block a user