diff --git a/CHANGELOG.md b/CHANGELOG.md index d3472bcdcf7..26d3eee80e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ BUG FIXES: * client/artifact: Honor netrc [GH-2524] * client/artifact: Handle tars where file in directory is listed before directory [GH-2524] + * driver/exec: Properly set file/dir ownership in chroots [GH-2552] * server: Reject non-TLS clients when TLS enabled [GH-2525] * server: Fix a panic in plan evaluation with partial failures and all_at_once set [GH-2544] diff --git a/client/allocdir/alloc_dir.go b/client/allocdir/alloc_dir.go index bbdb6431798..dd665926112 100644 --- a/client/allocdir/alloc_dir.go +++ b/client/allocdir/alloc_dir.go @@ -17,6 +17,12 @@ import ( "github.com/hpcloud/tail/watch" ) +const ( + // idUnsupported is what the uid/gid will be set to on platforms (eg + // Windows) that don't support integer ownership identifiers. + idUnsupported = -1 +) + var ( // The name of the directory that is shared across tasks in a task group. SharedAllocName = "alloc" @@ -385,7 +391,9 @@ func getFileWatcher(path string) watch.FileWatcher { return watch.NewPollingFileWatcher(path) } -func fileCopy(src, dst string, perm os.FileMode) error { +// fileCopy from src to dst setting the permissions and owner (if uid & gid are +// both greater than 0) +func fileCopy(src, dst string, uid, gid int, perm os.FileMode) error { // Do a simple copy. srcFile, err := os.Open(src) if err != nil { @@ -400,7 +408,13 @@ func fileCopy(src, dst string, perm os.FileMode) error { defer dstFile.Close() if _, err := io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err) + return fmt.Errorf("Couldn't copy %q to %q: %v", src, dst, err) + } + + if uid != idUnsupported && gid != idUnsupported { + if err := dstFile.Chown(uid, gid); err != nil { + return fmt.Errorf("Couldn't copy %q to %q: %v", src, dst, err) + } } return nil @@ -448,6 +462,12 @@ func createDir(basePath, relPath string) error { if err := os.MkdirAll(destDir, fi.Perm); err != nil { return err } + + if fi.Uid != idUnsupported && fi.Gid != idUnsupported { + if err := os.Chown(destDir, fi.Uid, fi.Gid); err != nil { + return err + } + } } return nil } @@ -456,6 +476,10 @@ func createDir(basePath, relPath string) error { type fileInfo struct { Name string Perm os.FileMode + + // Uid and Gid are unsupported on Windows + Uid int + Gid int } // splitPath stats each subdirectory of a path. The first element of the array @@ -463,17 +487,19 @@ type fileInfo struct { // path. func splitPath(path string) ([]fileInfo, error) { var mode os.FileMode - i, err := os.Stat(path) + fi, err := os.Stat(path) // If the path is not present in the host then we respond with the most // flexible permission. + uid, gid := idUnsupported, idUnsupported if err != nil { mode = os.ModePerm } else { - mode = i.Mode() + uid, gid = getOwner(fi) + mode = fi.Mode() } var dirs []fileInfo - dirs = append(dirs, fileInfo{Name: path, Perm: mode}) + dirs = append(dirs, fileInfo{Name: path, Perm: mode, Uid: uid, Gid: gid}) currentDir := path for { dir := filepath.Dir(filepath.Clean(currentDir)) @@ -483,13 +509,15 @@ func splitPath(path string) ([]fileInfo, error) { // We try to find the permission of the file in the host. If the path is not // present in the host then we respond with the most flexible permission. - i, err = os.Stat(dir) + uid, gid := idUnsupported, idUnsupported + fi, err := os.Stat(dir) if err != nil { mode = os.ModePerm } else { - mode = i.Mode() + uid, gid = getOwner(fi) + mode = fi.Mode() } - dirs = append(dirs, fileInfo{Name: dir, Perm: mode}) + dirs = append(dirs, fileInfo{Name: dir, Perm: mode, Uid: uid, Gid: gid}) currentDir = dir } return dirs, nil diff --git a/client/allocdir/fs_unix.go b/client/allocdir/fs_unix.go index 648fce1adb6..77447a32f7f 100644 --- a/client/allocdir/fs_unix.go +++ b/client/allocdir/fs_unix.go @@ -8,6 +8,7 @@ import ( "os/user" "path/filepath" "strconv" + "syscall" "golang.org/x/sys/unix" ) @@ -82,7 +83,7 @@ func getGid(u *user.User) (int, error) { // linkOrCopy attempts to hardlink dst to src and fallsback to copying if the // hardlink fails. -func linkOrCopy(src, dst string, perm os.FileMode) error { +func linkOrCopy(src, dst string, uid, gid int, perm os.FileMode) error { // Avoid link/copy if the file already exists in the chroot // TODO 0.6 clean this up. This was needed because chroot creation fails // when a process restarts. @@ -94,5 +95,13 @@ func linkOrCopy(src, dst string, perm os.FileMode) error { return nil } - return fileCopy(src, dst, perm) + return fileCopy(src, dst, uid, gid, perm) +} + +func getOwner(fi os.FileInfo) (int, int) { + stat, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return -1, -1 + } + return int(stat.Uid), int(stat.Gid) } diff --git a/client/allocdir/fs_windows.go b/client/allocdir/fs_windows.go index de0fcec898c..3667c1953bb 100644 --- a/client/allocdir/fs_windows.go +++ b/client/allocdir/fs_windows.go @@ -21,8 +21,8 @@ var ( ) // linkOrCopy is always copies dst to src on Windows. -func linkOrCopy(src, dst string, perm os.FileMode) error { - return fileCopy(src, dst, perm) +func linkOrCopy(src, dst string, uid, gid int, perm os.FileMode) error { + return fileCopy(src, dst, uid, gid, perm) } // The windows version does nothing currently. @@ -70,3 +70,8 @@ func MountSpecialDirs(taskDir string) error { func unmountSpecialDirs(taskDir string) error { return nil } + +// getOwner doesn't work on Windows as Windows doesn't use int user IDs +func getOwner(os.FileInfo) (int, int) { + return idUnsupported, idUnsupported +} diff --git a/client/allocdir/task_dir.go b/client/allocdir/task_dir.go index 4f2817e625c..b4376057213 100644 --- a/client/allocdir/task_dir.go +++ b/client/allocdir/task_dir.go @@ -163,7 +163,8 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { // Copy the file. taskEntry := filepath.Join(t.Dir, dest) - if err := linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil { + uid, gid := getOwner(s) + if err := linkOrCopy(source, taskEntry, uid, gid, s.Mode().Perm()); err != nil { return err } @@ -217,7 +218,8 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { continue } - if err := linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil { + uid, gid := getOwner(entry) + if err := linkOrCopy(hostEntry, taskEntry, uid, gid, entry.Mode().Perm()); err != nil { return err } }