walk() checks each directory in a relative path, starting from the initial parent path, and optionally creates any missing directories in the path. If an existing file or something else other than a directory conflicts with a directory in the path, walk() returns an error. If the create option is
(create bool)
| 53 | // implementation, walk() does not guard against TOCTOU (time-of-check/ |
| 54 | // time-of-use) races, as the methods of the os.Root type do. |
| 55 | func (w *DirWalker) walk(create bool) error { |
| 56 | currentPath := w.parentPath |
| 57 | |
| 58 | n := len(w.path) |
| 59 | for n > 0 { |
| 60 | currentDir := w.path |
| 61 | nextDirIndex := n |
| 62 | i := strings.IndexByte(w.path, '/') |
| 63 | if i >= 0 { |
| 64 | currentDir = w.path[0:i] |
| 65 | nextDirIndex = i + 1 |
| 66 | } |
| 67 | |
| 68 | // These should never occur in Git paths. |
| 69 | if currentDir == "" || currentDir == "." || currentDir == ".." { |
| 70 | return errors.Join(errors.New(tr.Tr.Get("invalid directory %q in path: %q", currentDir, w.path)), errInvalidDir) |
| 71 | } |
| 72 | |
| 73 | if currentPath == "" { |
| 74 | currentPath = currentDir |
| 75 | } else { |
| 76 | currentPath += "/" + currentDir |
| 77 | } |
| 78 | |
| 79 | stat, err := os.Lstat(currentPath) |
| 80 | if err != nil { |
| 81 | if !os.IsNotExist(err) || !create { |
| 82 | return err |
| 83 | } |
| 84 | |
| 85 | err = Mkdir(currentPath, w.config) |
| 86 | if err != nil { |
| 87 | return err |
| 88 | } |
| 89 | } else if !stat.Mode().IsDir() { |
| 90 | return errors.Join(errors.New(tr.Tr.Get("not a directory: %q", currentPath)), errNotDir) |
| 91 | } |
| 92 | |
| 93 | w.parentPath = currentPath |
| 94 | w.path = w.path[nextDirIndex:] |
| 95 | n -= nextDirIndex |
| 96 | } |
| 97 | |
| 98 | return nil |
| 99 | } |
| 100 | |
| 101 | func (w *DirWalker) Walk() error { |
| 102 | return w.walk(false) |