idtools.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. package fileutils
  2. import (
  3. "os"
  4. "path/filepath"
  5. "syscall"
  6. )
  7. // MkdirAllNewAs creates a directory (include any along the path) and then modifies
  8. // ownership ONLY of newly created directories to the requested uid/gid. If the
  9. // directories along the path exist, no change of ownership will be performed
  10. func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
  11. // make an array containing the original path asked for, plus (for mkAll == true)
  12. // all path components leading up to the complete path that don't exist before we MkdirAll
  13. // so that we can chown all of them properly at the end. If chownExisting is false, we won't
  14. // chown the full directory path if it exists
  15. var paths []string
  16. st, err := os.Stat(path)
  17. if err != nil && os.IsNotExist(err) {
  18. paths = []string{path}
  19. } else if err == nil {
  20. if !st.IsDir() {
  21. return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
  22. }
  23. // nothing to do; directory path fully exists already
  24. return nil
  25. }
  26. // walk back to "/" looking for directories which do not exist
  27. // and add them to the paths array for chown after creation
  28. dirPath := path
  29. for {
  30. dirPath = filepath.Dir(dirPath)
  31. if dirPath == "/" {
  32. break
  33. }
  34. if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
  35. paths = append(paths, dirPath)
  36. }
  37. }
  38. if err := os.MkdirAll(path, mode); err != nil {
  39. return err
  40. }
  41. // even if it existed, we will chown the requested path + any subpaths that
  42. // didn't exist when we called MkdirAll
  43. for _, pathComponent := range paths {
  44. if err := os.Chown(pathComponent, ownerUID, ownerGID); err != nil {
  45. return err
  46. }
  47. }
  48. return nil
  49. }