(ctx context.Context, r *http.Request, path string)
| 335 | } |
| 336 | |
| 337 | func (api *API) writeFile(ctx context.Context, r *http.Request, path string) (HTTPResponseCode, error) { |
| 338 | if !filepath.IsAbs(path) { |
| 339 | return http.StatusBadRequest, xerrors.Errorf("file path must be absolute: %q", path) |
| 340 | } |
| 341 | |
| 342 | resolved, err := api.resolvePath(path) |
| 343 | if err != nil { |
| 344 | return http.StatusInternalServerError, xerrors.Errorf("resolve symlink %q: %w", path, err) |
| 345 | } |
| 346 | path = resolved |
| 347 | |
| 348 | dir := filepath.Dir(path) |
| 349 | err = api.filesystem.MkdirAll(dir, 0o755) |
| 350 | if err != nil { |
| 351 | status := http.StatusInternalServerError |
| 352 | switch { |
| 353 | case errors.Is(err, os.ErrPermission): |
| 354 | status = http.StatusForbidden |
| 355 | case errors.Is(err, syscall.ENOTDIR): |
| 356 | status = http.StatusBadRequest |
| 357 | } |
| 358 | return status, err |
| 359 | } |
| 360 | |
| 361 | // Check if the target already exists so we can preserve its |
| 362 | // permissions on the temp file before rename. |
| 363 | var mode *os.FileMode |
| 364 | if stat, serr := api.filesystem.Stat(path); serr == nil { |
| 365 | if stat.IsDir() { |
| 366 | return http.StatusBadRequest, xerrors.Errorf("open %s: is a directory", path) |
| 367 | } |
| 368 | m := stat.Mode() |
| 369 | mode = &m |
| 370 | } |
| 371 | |
| 372 | return api.atomicWrite(ctx, path, mode, r.Body) |
| 373 | } |
| 374 | |
| 375 | func (api *API) HandleEditFiles(rw http.ResponseWriter, r *http.Request) { |
| 376 | ctx := r.Context() |
no test coverage detected