Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f7561dcd0 |
@@ -7,6 +7,7 @@ import (
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ErrDataDirOutsideVolume is returned by PrepareDataDir for a DATA_DIR
|
||||
@@ -26,9 +27,11 @@ var ErrDataDirOutsideVolume = errors.New(
|
||||
// volume: it follows a link only when it is written as a relative
|
||||
// path that stays inside volume, and refuses any other. A process on
|
||||
// the host can swap a link onto a path in volume at any moment while
|
||||
// this runs, but the os.Root checks each link as it reaches it, so the
|
||||
// most that can do is make a step fail or act on something else
|
||||
// inside volume.
|
||||
// this runs. Even then, the os.Root checks each link as it reaches it,
|
||||
// MkdirAll never creates a directory through a link, Lchown never
|
||||
// changes what a link points to, and the modes are set on directories
|
||||
// already opened (see chmodDir), so the most that process can do is
|
||||
// make a step fail or wait, or act on something else inside volume.
|
||||
func PrepareDataDir(volume, dir string, owner *user.User) error {
|
||||
// rel is dir as a path from volume; IsLocal is false for one that
|
||||
// leads out of it.
|
||||
@@ -73,10 +76,38 @@ func PrepareDataDir(volume, dir string, owner *user.User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = root.Chmod(".", dirPerms)
|
||||
err = chmodDir(root, ".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return root.Chmod(rel, dirPerms)
|
||||
return chmodDir(root, rel)
|
||||
}
|
||||
|
||||
// chmodDir gives name, a directory inside root, the mode the server
|
||||
// gives a directory it creates. Root.Chmod would not hold: on Linux it
|
||||
// checks that name is not a symbolic link, then sets the mode by name,
|
||||
// following a link swapped in between. So chmodDir opens name through
|
||||
// root and sets the mode on the open directory. It refuses anything
|
||||
// but a directory: a directory has no second name (hard link), so the
|
||||
// one opened is inside root, where any other file could be a hard link
|
||||
// to one outside.
|
||||
func chmodDir(root *os.Root, name string) error {
|
||||
dir, err := root.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = dir.Close() }()
|
||||
|
||||
info, err := dir.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return &fs.PathError{Op: "chmod", Path: name, Err: syscall.ENOTDIR}
|
||||
}
|
||||
|
||||
return dir.Chmod(dirPerms)
|
||||
}
|
||||
|
||||
@@ -93,6 +93,27 @@ func TestPrepareDataDirCreatesDataDir(t *testing.T) {
|
||||
requireMode(t, dir, fs.ModeDir|0o750)
|
||||
}
|
||||
|
||||
// TestPrepareDataDirSetsModeOfExistingDataDir: a DATA_DIR already on
|
||||
// the host with another mode gets the mode too, not only a new one.
|
||||
func TestPrepareDataDirSetsModeOfExistingDataDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
volume := t.TempDir()
|
||||
dir := filepath.Join(volume, reports)
|
||||
|
||||
err := os.Mkdir(dir, 0o700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = reportbuf.PrepareDataDir(volume, dir, currentUser())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
requireMode(t, dir, fs.ModeDir|0o750)
|
||||
}
|
||||
|
||||
func TestPrepareDataDirTakesTheVolumeItself(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user