diff --git a/client/allocdir/alloc_dir.go b/client/allocdir/alloc_dir.go index 6760507ef..fd4d60610 100644 --- a/client/allocdir/alloc_dir.go +++ b/client/allocdir/alloc_dir.go @@ -28,6 +28,18 @@ const ( // idUnsupported is what the uid/gid will be set to on platforms (eg // Windows) that don't support integer ownership identifiers. idUnsupported = -1 + + // fileMode777 is a constant that represents the file mode rwxrwxrwx + fileMode777 = os.FileMode(0o777) + + // fileMode710 is a constant that represents the file mode rwx--x--- + fileMode710 = os.FileMode(0o710) + + // fileMode755 is a constant that represents the file mode rwxr-xr-x + fileMode755 = os.FileMode(0o755) + + // fileMode666 is a constant that represents the file mode rw-rw-rw- + fileMode666 = os.FileMode(0o666) ) var ( @@ -65,7 +77,7 @@ var ( TaskPrivate = "private" // TaskDirs is the set of directories created in each tasks directory. - TaskDirs = map[string]os.FileMode{TmpDirName: os.ModeSticky | 0777} + TaskDirs = map[string]os.FileMode{TmpDirName: os.ModeSticky | fileMode777} // AllocGRPCSocket is the path relative to the task dir root for the // unix socket connected to Consul's gRPC endpoint. @@ -210,7 +222,7 @@ func (d *AllocDir) Snapshot(w io.Writer) error { } hdr, err := tar.FileInfoHeader(fileInfo, link) if err != nil { - return fmt.Errorf("error creating file header: %v", err) + return fmt.Errorf("error creating file header: %w", err) } hdr.Name = relPath if err := tw.WriteHeader(hdr); err != nil { @@ -248,7 +260,7 @@ func (d *AllocDir) Snapshot(w io.Writer) error { // anyway. d.logger.Warn("snapshotting failed and unable to write error marker", "error", writeErr) } - return fmt.Errorf("failed to snapshot %s: %v", path, err) + return fmt.Errorf("failed to snapshot %s: %w", path, err) } } @@ -273,7 +285,7 @@ func (d *AllocDir) Move(other Interface, tasks []*structs.Task) error { if fileInfo, err := os.Stat(otherDataDir); fileInfo != nil && err == nil { os.Remove(dataDir) // remove an empty data dir if it exists if err := os.Rename(otherDataDir, dataDir); err != nil { - return fmt.Errorf("error moving data dir: %v", err) + return fmt.Errorf("error moving data dir: %w", err) } } @@ -286,13 +298,13 @@ func (d *AllocDir) Move(other Interface, tasks []*structs.Task) error { if fileInfo != nil && err == nil { // TaskDirs haven't been built yet, so create it newTaskDir := filepath.Join(d.AllocDir, task.Name) - if err := os.MkdirAll(newTaskDir, 0777); err != nil { - return fmt.Errorf("error creating task %q dir: %v", task.Name, err) + if err := os.MkdirAll(newTaskDir, fileMode777); err != nil { + return fmt.Errorf("error creating task %q dir: %w", task.Name, err) } localDir := filepath.Join(newTaskDir, TaskLocal) os.Remove(localDir) // remove an empty local dir if it exists if err := os.Rename(otherTaskLocal, localDir); err != nil { - return fmt.Errorf("error moving task %q local dir: %v", task.Name, err) + return fmt.Errorf("error moving task %q local dir: %w", task.Name, err) } } } @@ -303,13 +315,13 @@ func (d *AllocDir) Move(other Interface, tasks []*structs.Task) error { // Destroy tears down previously build directory structure. func (d *AllocDir) Destroy() error { // Unmount all mounted shared alloc dirs. - var mErr multierror.Error + mErr := new(multierror.Error) if err := d.UnmountAll(); err != nil { - mErr.Errors = append(mErr.Errors, err) + mErr = multierror.Append(mErr, err) } if err := os.RemoveAll(d.AllocDir); err != nil { - mErr.Errors = append(mErr.Errors, fmt.Errorf("failed to remove alloc dir %q: %v", d.AllocDir, err)) + mErr = multierror.Append(mErr, fmt.Errorf("failed to remove alloc dir %q: %w", d.AllocDir, err)) } // Unset built since the alloc dir has been destroyed. @@ -324,52 +336,10 @@ func (d *AllocDir) UnmountAll() error { d.mu.RLock() defer d.mu.RUnlock() - var mErr multierror.Error + mErr := new(multierror.Error) for _, dir := range d.TaskDirs { - // Check if the directory has the shared alloc mounted. - if pathExists(dir.SharedTaskDir) { - if err := unlinkDir(dir.SharedTaskDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to unmount shared alloc dir %q: %v", dir.SharedTaskDir, err)) - } else if err := os.RemoveAll(dir.SharedTaskDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to delete shared alloc dir %q: %v", dir.SharedTaskDir, err)) - } - } - - if pathExists(dir.SecretsDir) { - if err := removeSecretDir(dir.SecretsDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to remove the secret dir %q: %v", dir.SecretsDir, err)) - } - } - - if pathExists(dir.PrivateDir) { - if err := removeSecretDir(dir.PrivateDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to remove the private dir %q: %v", dir.PrivateDir, err)) - } - } - - if pathExists(dir.MountsAllocDir) { - if err := unlinkDir(dir.MountsAllocDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to remove the alloc mounts dir %q: %v", dir.MountsAllocDir, err), - ) - } - } - - if pathExists(dir.MountsTaskDir) { - if err := unlinkDir(dir.MountsTaskDir); err != nil { - mErr.Errors = append(mErr.Errors, - fmt.Errorf("failed to remove the alloc mounts task dir %q: %v", dir.MountsTaskDir, err), - ) - } - } - - // Unmount dev/ and proc/ have been mounted. - if err := dir.unmountSpecialDirs(); err != nil { - mErr.Errors = append(mErr.Errors, err) + if err := dir.Unmount(); err != nil { + mErr = multierror.Append(mErr, err) } } @@ -379,27 +349,19 @@ func (d *AllocDir) UnmountAll() error { // Build the directory tree for an allocation. func (d *AllocDir) Build() error { // Make the alloc directory, owned by the nomad process. - if err := os.MkdirAll(d.AllocDir, 0755); err != nil { - return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err) + if err := os.MkdirAll(d.AllocDir, fileMode755); err != nil { + return fmt.Errorf("Failed to make the alloc directory %v: %w", d.AllocDir, err) } // Make the shared directory and make it available to all user/groups. - if err := os.MkdirAll(d.SharedDir, 0777); err != nil { - return err - } - - // Make the shared directory have non-root permissions. - if err := dropDirPermissions(d.SharedDir, os.ModePerm); err != nil { + if err := allocMkdirAll(d.SharedDir, fileMode755); err != nil { return err } // Create shared subdirs for _, dir := range SharedAllocDirs { p := filepath.Join(d.SharedDir, dir) - if err := os.MkdirAll(p, 0777); err != nil { - return err - } - if err := dropDirPermissions(p, os.ModePerm); err != nil { + if err := allocMkdirAll(p, fileMode777); err != nil { return err } } @@ -414,7 +376,7 @@ func (d *AllocDir) Build() error { // List returns the list of files at a path relative to the alloc dir func (d *AllocDir) List(path string) ([]*cstructs.AllocFileInfo, error) { if escapes, err := escapingfs.PathEscapesAllocDir(d.AllocDir, "", path); err != nil { - return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err) + return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %w", err) } else if escapes { return nil, fmt.Errorf("Path escapes the alloc directory") } @@ -444,7 +406,7 @@ func (d *AllocDir) List(path string) ([]*cstructs.AllocFileInfo, error) { // Stat returns information about the file at a path relative to the alloc dir func (d *AllocDir) Stat(path string) (*cstructs.AllocFileInfo, error) { if escapes, err := escapingfs.PathEscapesAllocDir(d.AllocDir, "", path); err != nil { - return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err) + return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %w", err) } else if escapes { return nil, fmt.Errorf("Path escapes the alloc directory") } @@ -494,7 +456,7 @@ func detectContentType(fileInfo os.FileInfo, path string) string { // ReadAt returns a reader for a file at the path relative to the alloc dir func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) { if escapes, err := escapingfs.PathEscapesAllocDir(d.AllocDir, "", path); err != nil { - return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err) + return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %w", err) } else if escapes { return nil, fmt.Errorf("Path escapes the alloc directory") } @@ -520,7 +482,7 @@ func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) { return nil, err } if _, err := f.Seek(offset, 0); err != nil { - return nil, fmt.Errorf("can't seek to offset %q: %v", offset, err) + return nil, fmt.Errorf("can't seek to offset %q: %w", offset, err) } return f, nil } @@ -529,7 +491,7 @@ func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) { // directory exists. The block can be cancelled with the passed context. func (d *AllocDir) BlockUntilExists(ctx context.Context, path string) (chan error, error) { if escapes, err := escapingfs.PathEscapesAllocDir(d.AllocDir, "", path); err != nil { - return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err) + return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %w", err) } else if escapes { return nil, fmt.Errorf("Path escapes the alloc directory") } @@ -555,7 +517,7 @@ func (d *AllocDir) BlockUntilExists(ctx context.Context, path string) (chan erro // used to clean up the watch. func (d *AllocDir) ChangeEvents(ctx context.Context, path string, curOffset int64) (*watch.FileChanges, error) { if escapes, err := escapingfs.PathEscapesAllocDir(d.AllocDir, "", path); err != nil { - return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err) + return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %w", err) } else if escapes { return nil, fmt.Errorf("Path escapes the alloc directory") } @@ -583,23 +545,23 @@ func fileCopy(src, dst string, uid, gid int, perm os.FileMode) error { // Do a simple copy. srcFile, err := os.Open(src) if err != nil { - return fmt.Errorf("Couldn't open src file %v: %v", src, err) + return fmt.Errorf("Couldn't open src file %v: %w", src, err) } defer srcFile.Close() dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, perm) if err != nil { - return fmt.Errorf("Couldn't create destination file %v: %v", dst, err) + return fmt.Errorf("Couldn't create destination file %v: %w", dst, err) } defer dstFile.Close() if _, err := io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("Couldn't copy %q to %q: %v", src, dst, err) + return fmt.Errorf("Couldn't copy %q to %q: %w", 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 fmt.Errorf("Couldn't copy %q to %q: %w", src, dst, err) } } @@ -679,7 +641,7 @@ func splitPath(path string) ([]fileInfo, error) { // flexible permission. uid, gid := idUnsupported, idUnsupported if err != nil { - mode = os.ModePerm + mode = fileMode777 } else { uid, gid = getOwner(fi) mode = fi.Mode() @@ -698,7 +660,7 @@ func splitPath(path string) ([]fileInfo, error) { uid, gid := idUnsupported, idUnsupported fi, err := os.Stat(dir) if err != nil { - mode = os.ModePerm + mode = fileMode777 } else { uid, gid = getOwner(fi) mode = fi.Mode() @@ -721,7 +683,7 @@ func writeError(tw *tar.Writer, allocID string, err error) error { contents := []byte(fmt.Sprintf("Error snapshotting: %v", err)) hdr := tar.Header{ Name: SnapshotErrorFilename(allocID), - Mode: 0666, + Mode: int64(fileMode666), Size: int64(len(contents)), AccessTime: SnapshotErrorTime, ChangeTime: SnapshotErrorTime, @@ -736,3 +698,32 @@ func writeError(tw *tar.Writer, allocID string, err error) error { _, err = tw.Write(contents) return err } + +// allocMkdirAll creates a directory and sets the permissions to the passed +// value. It also sets the owner of the directory to "nobody" on systems that +// allow. +func allocMkdirAll(path string, perms os.FileMode) error { + // Create the directory + if err := os.MkdirAll(path, perms); err != nil { + return err + } + // Update the access permissions on the directory + if err := dropDirPermissions(path, perms); err != nil { + return err + } + return nil +} + +// allocMakeSecretsDir creates a directory for sensitive items such as secrets. +// When possible it uses a tmpfs or some other method to prevent it from +// persisting to actual disk. +func allocMakeSecretsDir(path string, perms os.FileMode) error { + // Create the private directory + if err := createSecretDir(path); err != nil { + return err + } + if err := dropDirPermissions(path, perms); err != nil { + return err + } + return nil +} diff --git a/client/allocdir/fs_darwin.go b/client/allocdir/fs_darwin.go index c749c3cad..edd8c8958 100644 --- a/client/allocdir/fs_darwin.go +++ b/client/allocdir/fs_darwin.go @@ -20,7 +20,7 @@ func unlinkDir(dir string) error { // createSecretDir creates the secrets dir folder at the given path func createSecretDir(dir string) error { - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // removeSecretDir removes the secrets dir folder diff --git a/client/allocdir/fs_freebsd.go b/client/allocdir/fs_freebsd.go index c749c3cad..edd8c8958 100644 --- a/client/allocdir/fs_freebsd.go +++ b/client/allocdir/fs_freebsd.go @@ -20,7 +20,7 @@ func unlinkDir(dir string) error { // createSecretDir creates the secrets dir folder at the given path func createSecretDir(dir string) error { - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // removeSecretDir removes the secrets dir folder diff --git a/client/allocdir/fs_linux.go b/client/allocdir/fs_linux.go index 3dae8c5d0..cbf7b993a 100644 --- a/client/allocdir/fs_linux.go +++ b/client/allocdir/fs_linux.go @@ -26,7 +26,7 @@ const ( // linkDir bind mounts src to dst as Linux doesn't support hardlinking // directories. func linkDir(src, dst string) error { - if err := os.MkdirAll(dst, 0777); err != nil { + if err := os.MkdirAll(dst, fileMode777); err != nil { return err } @@ -62,7 +62,7 @@ func unlinkDir(dir string) error { func createSecretDir(dir string) error { // Only mount the tmpfs if we are root if unix.Geteuid() == 0 { - if err := os.MkdirAll(dir, 0777); err != nil { + if err := os.MkdirAll(dir, fileMode777); err != nil { return err } @@ -79,7 +79,7 @@ func createSecretDir(dir string) error { } // Create the marker file so we don't try to mount more than once - f, err := os.OpenFile(marker, os.O_RDWR|os.O_CREATE, 0666) + f, err := os.OpenFile(marker, os.O_RDWR|os.O_CREATE, fileMode666) if err != nil { // Hard fail since if this fails something is really wrong return err @@ -88,7 +88,7 @@ func createSecretDir(dir string) error { return nil } - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // createSecretDir removes the secrets dir folder diff --git a/client/allocdir/fs_netbsd.go b/client/allocdir/fs_netbsd.go index c749c3cad..edd8c8958 100644 --- a/client/allocdir/fs_netbsd.go +++ b/client/allocdir/fs_netbsd.go @@ -20,7 +20,7 @@ func unlinkDir(dir string) error { // createSecretDir creates the secrets dir folder at the given path func createSecretDir(dir string) error { - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // removeSecretDir removes the secrets dir folder diff --git a/client/allocdir/fs_solaris.go b/client/allocdir/fs_solaris.go index 1c3569624..d35be1f7e 100644 --- a/client/allocdir/fs_solaris.go +++ b/client/allocdir/fs_solaris.go @@ -21,7 +21,7 @@ func unlinkDir(dir string) error { // createSecretDir creates the secrets dir folder at the given path func createSecretDir(dir string) error { // TODO solaris has support for tmpfs so use that - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // removeSecretDir removes the secrets dir folder diff --git a/client/allocdir/fs_unix.go b/client/allocdir/fs_unix.go index d3ad3b3ce..b393837be 100644 --- a/client/allocdir/fs_unix.go +++ b/client/allocdir/fs_unix.go @@ -34,8 +34,8 @@ var ( // dropDirPermissions gives full access to a directory to all users and sets // the owner to nobody. func dropDirPermissions(path string, desired os.FileMode) error { - if err := os.Chmod(path, desired|0777); err != nil { - return fmt.Errorf("Chmod(%v) failed: %v", path, err) + if err := os.Chmod(path, desired|fileMode777); err != nil { + return fmt.Errorf("Chmod(%v) failed: %w", path, err) } // Can't change owner if not root. @@ -59,7 +59,7 @@ func dropDirPermissions(path string, desired os.FileMode) error { } if err := os.Chown(path, uid, gid); err != nil { - return fmt.Errorf("Couldn't change owner/group of %v to (uid: %v, gid: %v): %v", path, uid, gid, err) + return fmt.Errorf("Couldn't change owner/group of %v to (uid: %v, gid: %v): %w", path, uid, gid, err) } return nil @@ -69,7 +69,7 @@ func dropDirPermissions(path string, desired os.FileMode) error { func getUid(u *user.User) (int, error) { uid, err := strconv.Atoi(u.Uid) if err != nil { - return 0, fmt.Errorf("Unable to convert Uid to an int: %v", err) + return 0, fmt.Errorf("Unable to convert Uid to an int: %w", err) } return uid, nil @@ -79,7 +79,7 @@ func getUid(u *user.User) (int, error) { func getGid(u *user.User) (int, error) { gid, err := strconv.Atoi(u.Gid) if err != nil { - return 0, fmt.Errorf("Unable to convert Gid to an int: %v", err) + return 0, fmt.Errorf("Unable to convert Gid to an int: %w", err) } return gid, nil diff --git a/client/allocdir/fs_windows.go b/client/allocdir/fs_windows.go index 7794abb36..3e3223810 100644 --- a/client/allocdir/fs_windows.go +++ b/client/allocdir/fs_windows.go @@ -39,7 +39,7 @@ func unlinkDir(dir string) error { // createSecretDir creates the secrets dir folder at the given path func createSecretDir(dir string) error { - return os.MkdirAll(dir, 0777) + return os.MkdirAll(dir, fileMode777) } // removeSecretDir removes the secrets dir folder diff --git a/client/allocdir/task_dir.go b/client/allocdir/task_dir.go index 4270b5d4d..644bd6c7a 100644 --- a/client/allocdir/task_dir.go +++ b/client/allocdir/task_dir.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-set/v2" "github.com/hashicorp/nomad/helper/users/dynamic" "github.com/hashicorp/nomad/plugins/drivers/fsisolation" @@ -107,32 +108,19 @@ func (d *AllocDir) newTaskDir(taskName string) *TaskDir { // allows skipping chroot creation if the caller knows it has already been // done. client.alloc_dir will be skipped. func (t *TaskDir) Build(fsi fsisolation.Mode, chroot map[string]string, username string) error { - if err := os.MkdirAll(t.Dir, 0777); err != nil { + if err := allocMkdirAll(t.Dir, fileMode777); err != nil { return err } - // Make the task directory have non-root permissions. - if err := dropDirPermissions(t.Dir, os.ModePerm); err != nil { - return err - } - - // Create a local directory that each task can use. - if err := os.MkdirAll(t.LocalDir, 0777); err != nil { - return err - } - - if err := dropDirPermissions(t.LocalDir, os.ModePerm); err != nil { + if err := allocMkdirAll(t.LocalDir, fileMode777); err != nil { return err } // Create the directories that should be in every task. for dir, perms := range TaskDirs { absdir := filepath.Join(t.Dir, dir) - if err := os.MkdirAll(absdir, perms); err != nil { - return err - } - if err := dropDirPermissions(absdir, perms); err != nil { + if err := allocMkdirAll(absdir, perms); err != nil { return err } } @@ -146,26 +134,18 @@ func (t *TaskDir) Build(fsi fsisolation.Mode, chroot map[string]string, username empty, _ := pathEmpty(t.SharedTaskDir) if !pathExists(t.SharedTaskDir) || empty { if err := linkDir(t.SharedAllocDir, t.SharedTaskDir); err != nil { - return fmt.Errorf("Failed to mount shared directory for task: %v", err) + return fmt.Errorf("Failed to mount shared directory for task: %w", err) } } } // Create the secret directory - if err := createSecretDir(t.SecretsDir); err != nil { - return err - } - - if err := dropDirPermissions(t.SecretsDir, os.ModePerm); err != nil { + if err := allocMakeSecretsDir(t.SecretsDir, fileMode777); err != nil { return err } // Create the private directory - if err := createSecretDir(t.PrivateDir); err != nil { - return err - } - - if err := dropDirPermissions(t.PrivateDir, os.ModePerm); err != nil { + if err := allocMakeSecretsDir(t.PrivateDir, fileMode777); err != nil { return err } @@ -185,7 +165,7 @@ func (t *TaskDir) Build(fsi fsisolation.Mode, chroot map[string]string, username // create the task unique directory under the client mounts path parent := filepath.Dir(t.MountsAllocDir) - if err = os.MkdirAll(parent, 0o710); err != nil { + if err = os.MkdirAll(parent, fileMode710); err != nil { return fmt.Errorf("Failed to create task mount directory: %v", err) } if err = os.Chown(parent, uid, gid); err != nil { @@ -193,8 +173,8 @@ func (t *TaskDir) Build(fsi fsisolation.Mode, chroot map[string]string, username } // create the task and alloc mount points - mountDir(t.AllocDir, t.MountsAllocDir, uid, gid, 0o710) - mountDir(t.Dir, t.MountsTaskDir, uid, gid, 0o710) + mountDir(t.AllocDir, t.MountsAllocDir, uid, gid, fileMode710) + mountDir(t.Dir, t.MountsTaskDir, uid, gid, fileMode710) } return nil @@ -225,7 +205,7 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { // Embedding a single file if !s.IsDir() { if err := createDir(t.Dir, filepath.Dir(dest)); err != nil { - return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err) + return fmt.Errorf("Couldn't create destination directory %v: %w", dest, err) } // Copy the file. @@ -242,19 +222,19 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { destDir := filepath.Join(t.Dir, dest) if err := createDir(t.Dir, dest); err != nil { - return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err) + return fmt.Errorf("Couldn't create destination directory %v: %w", destDir, err) } // Enumerate the files in source. dirEntries, err := os.ReadDir(source) if err != nil { - return fmt.Errorf("Couldn't read directory %v: %v", source, err) + return fmt.Errorf("Couldn't read directory %v: %w", source, err) } for _, fileEntry := range dirEntries { entry, err := fileEntry.Info() if err != nil { - return fmt.Errorf("Couldn't read the file information %v: %v", entry, err) + return fmt.Errorf("Couldn't read the file information %v: %w", entry, err) } hostEntry := filepath.Join(source, entry.Name()) taskEntry := filepath.Join(destDir, filepath.Base(hostEntry)) @@ -277,13 +257,13 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { link, err := os.Readlink(hostEntry) if err != nil { - return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err) + return fmt.Errorf("Couldn't resolve symlink for %v: %w", source, err) } if err := os.Symlink(link, taskEntry); err != nil { // Symlinking twice if err.(*os.LinkError).Err.Error() != "file exists" { - return fmt.Errorf("Couldn't create symlink: %v", err) + return fmt.Errorf("Couldn't create symlink: %w", err) } } continue @@ -303,3 +283,55 @@ func (t *TaskDir) embedDirs(entries map[string]string) error { return nil } + +// Unmount or delete task directories. Returns all errors as a multierror. +func (t *TaskDir) Unmount() error { + mErr := new(multierror.Error) + + // Check if the directory has the shared alloc mounted. + if pathExists(t.SharedTaskDir) { + if err := unlinkDir(t.SharedTaskDir); err != nil { + mErr = multierror.Append(mErr, + fmt.Errorf("failed to unmount shared alloc dir %q: %w", t.SharedTaskDir, err)) + } else if err := os.RemoveAll(t.SharedTaskDir); err != nil { + mErr = multierror.Append(mErr, + fmt.Errorf("failed to delete shared alloc dir %q: %w", t.SharedTaskDir, err)) + } + } + + if pathExists(t.SecretsDir) { + if err := removeSecretDir(t.SecretsDir); err != nil { + mErr = multierror.Append(mErr, + fmt.Errorf("failed to remove the secret dir %q: %w", t.SecretsDir, err)) + } + } + + if pathExists(t.PrivateDir) { + if err := removeSecretDir(t.PrivateDir); err != nil { + mErr = multierror.Append(mErr, + fmt.Errorf("failed to remove the private dir %q: %w", t.PrivateDir, err)) + } + } + + if pathExists(t.MountsAllocDir) { + if err := unlinkDir(t.MountsAllocDir); err != nil { + mErr.Errors = append(mErr.Errors, + fmt.Errorf("failed to remove the alloc mounts dir %q: %w", t.MountsAllocDir, err), + ) + } + } + + if pathExists(t.MountsTaskDir) { + if err := unlinkDir(t.MountsTaskDir); err != nil { + mErr.Errors = append(mErr.Errors, + fmt.Errorf("failed to remove the alloc mounts task dir %q: %w", t.MountsTaskDir, err), + ) + } + } + + // Unmount dev/ and proc/ have been mounted. + if err := t.unmountSpecialDirs(); err != nil { + mErr = multierror.Append(mErr, err) + } + return mErr.ErrorOrNil() +} diff --git a/client/allocdir/task_dir_linux.go b/client/allocdir/task_dir_linux.go index 2cb652aab..d11747cd6 100644 --- a/client/allocdir/task_dir_linux.go +++ b/client/allocdir/task_dir_linux.go @@ -15,13 +15,13 @@ import ( // error is returned if the directories do not exist or have already been // unmounted. func (t *TaskDir) unmountSpecialDirs() error { - errs := new(multierror.Error) + mErr := new(multierror.Error) dev := filepath.Join(t.Dir, "dev") if pathExists(dev) { if err := unlinkDir(dev); err != nil { - errs = multierror.Append(errs, fmt.Errorf("Failed to unmount dev %q: %v", dev, err)) + mErr = multierror.Append(mErr, fmt.Errorf("Failed to unmount dev %q: %w", dev, err)) } else if err := os.RemoveAll(dev); err != nil { - errs = multierror.Append(errs, fmt.Errorf("Failed to delete dev directory %q: %v", dev, err)) + mErr = multierror.Append(mErr, fmt.Errorf("Failed to delete dev directory %q: %w", dev, err)) } } @@ -29,11 +29,11 @@ func (t *TaskDir) unmountSpecialDirs() error { proc := filepath.Join(t.Dir, "proc") if pathExists(proc) { if err := unlinkDir(proc); err != nil { - errs = multierror.Append(errs, fmt.Errorf("Failed to unmount proc %q: %v", proc, err)) + mErr = multierror.Append(mErr, fmt.Errorf("Failed to unmount proc %q: %w", proc, err)) } else if err := os.RemoveAll(proc); err != nil { - errs = multierror.Append(errs, fmt.Errorf("Failed to delete proc directory %q: %v", dev, err)) + mErr = multierror.Append(mErr, fmt.Errorf("Failed to delete proc directory %q: %w", dev, err)) } } - return errs.ErrorOrNil() + return mErr.ErrorOrNil() }