mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
pkg/atomicwriter: return early if parent directory is invalid
Rewrite `validateDestination` to first check if the destination path
exists. This slightly simplifies the logic (allowing returning
early in each step of the validation) and slightly improves the
error produced.
Before this, the error confusingly would mention the full path
not being a directory. While this _does_ match what `os.Writefile`
would return, it's .. confusing:
failed to stat output path: lstat ./not-a-dir/new-file.txt: not a directory
After this, the error would mention the directory that doesn't exist:
invalid output path: stat ./not-a-dir: not a directory
A slight optimization is made as well, now checking for _both_ "."
and ".." as special case, as either path should exist given any current
working directory (unless the working directory has been deleted, but we'd
fail further down the line).
With this change in order, we can also merge `validateFileMode` into
`validateDestination`.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/moby/sys/sequential"
|
||||
)
|
||||
@@ -16,26 +17,27 @@ func validateDestination(fileName string) error {
|
||||
if fileName == "" {
|
||||
return errors.New("file name is empty")
|
||||
}
|
||||
if dir := filepath.Dir(fileName); dir != "" && dir != "." && dir != ".." {
|
||||
di, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid output path: %w", err)
|
||||
}
|
||||
if !di.IsDir() {
|
||||
return fmt.Errorf("invalid output path: %w", &os.PathError{Op: "stat", Path: dir, Err: syscall.ENOTDIR})
|
||||
}
|
||||
}
|
||||
|
||||
// Deliberately using Lstat here to match the behavior of [os.Rename],
|
||||
// which is used when completing the write and does not resolve symlinks.
|
||||
if fi, err := os.Lstat(fileName); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to stat output path: %w", err)
|
||||
fi, err := os.Lstat(fileName)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
} else if err := validateFileMode(fi.Mode()); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to stat output path: %w", err)
|
||||
}
|
||||
if dir := filepath.Dir(fileName); dir != "" && dir != "." {
|
||||
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("invalid file path: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFileMode(mode os.FileMode) error {
|
||||
switch {
|
||||
switch mode := fi.Mode(); {
|
||||
case mode.IsRegular():
|
||||
return nil // Regular file
|
||||
case mode&os.ModeDir != 0:
|
||||
|
||||
Reference in New Issue
Block a user