(name string, perm os.FileMode)
| 289 | } |
| 290 | |
| 291 | func (r *Root) openDirectories(name string, perm os.FileMode) (*os.Root, error) { |
| 292 | var current string |
| 293 | parent := r.root |
| 294 | parentOwned := false |
| 295 | closeParent := func() { |
| 296 | if parentOwned { |
| 297 | _ = parent.Close() |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | if name == "." { |
| 302 | root, err := r.openVerifiedDirectory(parent, ".", ".") |
| 303 | if err != nil { |
| 304 | return nil, err |
| 305 | } |
| 306 | return root, nil |
| 307 | } |
| 308 | |
| 309 | for component := range strings.SplitSeq(name, string(filepath.Separator)) { |
| 310 | if component == "" || component == "." { |
| 311 | continue |
| 312 | } |
| 313 | current = filepath.Join(current, component) |
| 314 | |
| 315 | info, err := parent.Lstat(component) |
| 316 | if os.IsNotExist(err) { |
| 317 | if err := parent.Mkdir(component, perm); err != nil && !os.IsExist(err) { |
| 318 | closeParent() |
| 319 | return nil, err |
| 320 | } |
| 321 | info, err = parent.Lstat(component) |
| 322 | } |
| 323 | if err != nil { |
| 324 | closeParent() |
| 325 | return nil, err |
| 326 | } |
| 327 | redirect, redirectErr := isPathRedirect(r, parent, component, info) |
| 328 | if redirectErr != nil { |
| 329 | closeParent() |
| 330 | return nil, redirectErr |
| 331 | } |
| 332 | if redirect { |
| 333 | closeParent() |
| 334 | return nil, SymlinkTraversalError{Path: current} |
| 335 | } |
| 336 | if !info.IsDir() { |
| 337 | closeParent() |
| 338 | return nil, &os.PathError{Op: "open", Path: current, Err: fmt.Errorf("not a directory")} |
| 339 | } |
| 340 | |
| 341 | next, err := r.openVerifiedDirectory(parent, component, current) |
| 342 | if err != nil { |
| 343 | closeParent() |
| 344 | return nil, err |
| 345 | } |
| 346 | if parentOwned { |
| 347 | if err := parent.Close(); err != nil { |
| 348 | _ = next.Close() |
no test coverage detected