MCPcopy Index your code
hub / github.com/coder/coder / atomicWrite

Method atomicWrite

agent/agentfiles/files.go:569–625  ·  view source on GitHub ↗

atomicWrite writes content from r to path via a temp file in the same directory. If the target exists, its permissions are preserved. On failure the temp file is cleaned up and the original is untouched.

(ctx context.Context, path string, mode *os.FileMode, r io.Reader)

Source from the content-addressed store, hash-verified

567// On failure the temp file is cleaned up and the original is
568// untouched.
569func (api *API) atomicWrite(ctx context.Context, path string, mode *os.FileMode, r io.Reader) (int, error) {
570 logger := api.logger.With(agentchat.Fields(ctx)...)
571
572 dir := filepath.Dir(path)
573 tmpName := filepath.Join(dir, fmt.Sprintf(".%s.tmp.%s", filepath.Base(path), uuid.New().String()[:8]))
574
575 tmpfile, err := api.filesystem.OpenFile(tmpName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o666)
576 if err != nil {
577 status := http.StatusInternalServerError
578 if errors.Is(err, os.ErrPermission) {
579 status = http.StatusForbidden
580 }
581 return status, err
582 }
583
584 cleanup := func() {
585 if err := api.filesystem.Remove(tmpName); err != nil {
586 logger.Warn(ctx, "unable to clean up temp file", slog.Error(err))
587 }
588 }
589
590 _, err = io.Copy(tmpfile, r)
591 if err != nil {
592 _ = tmpfile.Close()
593 cleanup()
594 return http.StatusInternalServerError, xerrors.Errorf("write %s: %w", path, err)
595 }
596
597 // Close before rename to flush buffered data and catch write
598 // errors (e.g. delayed allocation failures).
599 if err := tmpfile.Close(); err != nil {
600 cleanup()
601 return http.StatusInternalServerError, xerrors.Errorf("write %s: %w", path, err)
602 }
603
604 // Set permissions on the temp file before rename so there is
605 // no window where the target has wrong permissions.
606 if mode != nil {
607 if err := api.filesystem.Chmod(tmpName, *mode); err != nil {
608 logger.Warn(ctx, "unable to set file permissions",
609 slog.F("path", path),
610 slog.Error(err),
611 )
612 }
613 }
614
615 if err := api.filesystem.Rename(tmpName, path); err != nil {
616 cleanup()
617 status := http.StatusInternalServerError
618 if errors.Is(err, os.ErrPermission) {
619 status = http.StatusForbidden
620 }
621 return status, xerrors.Errorf("write %s: %w", path, err)
622 }
623
624 return 0, nil
625}
626

Callers 2

writeFileMethod · 0.95
HandleEditFilesMethod · 0.95

Calls 11

FieldsFunction · 0.92
cleanupFunction · 0.85
NewMethod · 0.65
RemoveMethod · 0.65
CopyMethod · 0.65
CloseMethod · 0.65
StringMethod · 0.45
IsMethod · 0.45
ErrorMethod · 0.45
ErrorfMethod · 0.45
RenameMethod · 0.45

Tested by

no test coverage detected