Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions server/internal/cmd/filexfercli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,16 @@ const defaultFileListener = "127.0.0.1:3453"
const maxSyncRounds = 3

// pinchState computes all state file paths from a target output directory.
// Given targetDir="/var/lib/pinch/dst", state lives in the parent:
// Given targetDir="/var/lib/pinch/dst", state lives in a per-target subdir
// of the parent's .pinch directory so sibling transfers don't collide:
//
// /var/lib/pinch/.pinch/manifest ← client state: what's on disk (written by start/sync)
// /var/lib/pinch/.pinch/manifest.server ← server state: written by transfer, read by start/get
// /var/lib/pinch/.pinch/manifest.progress
// /var/lib/pinch/.pinch/remote/ (staging for start)
// /var/lib/pinch/.pinch/dst/manifest ← client state: what's on disk (written by start/sync)
// /var/lib/pinch/.pinch/dst/manifest.server ← server state: written by transfer, read by start/get
// /var/lib/pinch/.pinch/dst/manifest.progress
// /var/lib/pinch/.pinch/dst/remote/ (staging for start)
type pinchState struct {
TargetDir string // the user-facing output directory
StateDir string // parent/.pinch
StateDir string // parent/.pinch/<basename>
ManifestPath string // StateDir/manifest (client state: what's on disk)
ServerManifestPath string // StateDir/manifest.server (server state: from transfer)
ProgressPath string // StateDir/manifest.progress
Expand All @@ -225,7 +226,7 @@ func newPinchState(targetDir string) (*pinchState, error) {
if parent == targetDir {
return nil, fmt.Errorf("target directory %q has no distinct parent", targetDir)
}
stateDir := filepath.Join(parent, ".pinch")
stateDir := filepath.Join(parent, ".pinch", filepath.Base(targetDir))
return &pinchState{
TargetDir: targetDir,
StateDir: stateDir,
Expand Down Expand Up @@ -519,6 +520,7 @@ func cleanupCopyState(targetDir string, stderr io.Writer) int {
fmt.Fprintf(stderr, "remove state directory failed: %v\n", err)
return 1
}
_ = os.Remove(filepath.Dir(ps.StateDir))
return 0
}

Expand Down Expand Up @@ -1238,6 +1240,7 @@ func runTransfer(serverURL string, cfg transferArgs, stdout io.Writer, stderr io
fmt.Fprintf(stderr, "remove state directory failed: %v\n", err)
return 1
}
_ = os.Remove(filepath.Dir(ps.StateDir))

fmt.Fprintf(stderr, "transfer(addr=[%s], source=[%s])\n", serverURL, cfg.sourceDir)

Expand Down
22 changes: 11 additions & 11 deletions server/internal/cmd/filexfercli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func buildCLIFrameWithMetadata(fileID uint64, body []byte, offset int64, meta *F
func setupPinchState(t *testing.T, tmp string, manifestRaw string, progressRaw string) string {
t.Helper()
targetDir := filepath.Join(tmp, "dst")
pinchDir := filepath.Join(tmp, ".pinch")
pinchDir := filepath.Join(tmp, ".pinch", "dst")
if err := os.MkdirAll(pinchDir, 0o755); err != nil {
t.Fatalf("mkdir .pinch: %v", err)
}
Expand Down Expand Up @@ -437,7 +437,7 @@ func TestRunCLITransferAndGet(t *testing.T) {

var stdout bytes.Buffer
var stderr bytes.Buffer
serverManifestPath := filepath.Join(tmp, ".pinch", "manifest.server")
serverManifestPath := filepath.Join(tmp, ".pinch", "dst", "manifest.server")
code := runTransferCLI(srv.URL, []string{"-s", "/remote", targetDir}, &stdout, &stderr)
if code != 0 {
t.Fatalf("transfer: expected 0, got %d stderr=%s", code, stderr.String())
Expand Down Expand Up @@ -606,7 +606,7 @@ func TestRunCLITransferWithEncryptAuto(t *testing.T) {
if code != 0 {
t.Fatalf("transfer: expected 0, got %d stderr=%s", code, stderr.String())
}
serverManifestPath := filepath.Join(tmp, ".pinch", "manifest.server")
serverManifestPath := filepath.Join(tmp, ".pinch", "dst", "manifest.server")
raw, err := os.ReadFile(serverManifestPath)
if err != nil {
t.Fatalf("read manifest.server: %v", err)
Expand Down Expand Up @@ -665,7 +665,7 @@ func TestRunCLITransferWithEncryptAES(t *testing.T) {
if code != 0 {
t.Fatalf("transfer: expected 0, got %d stderr=%s", code, stderr.String())
}
serverManifestPath := filepath.Join(tmp, ".pinch", "manifest.server")
serverManifestPath := filepath.Join(tmp, ".pinch", "dst", "manifest.server")
raw, err := os.ReadFile(serverManifestPath)
if err != nil {
t.Fatalf("read manifest.server: %v", err)
Expand Down Expand Up @@ -844,10 +844,10 @@ func TestRunCLIStartDiscardSkipsTargetMutationAndLocalManifest(t *testing.T) {
if _, err := os.Stat(filepath.Join(targetDir, "a.txt")); !os.IsNotExist(err) {
t.Fatalf("expected discarded output to be absent, stat err=%v", err)
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "manifest")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst", "manifest")); !os.IsNotExist(err) {
t.Fatalf("expected local manifest to be absent, stat err=%v", err)
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "manifest.progress")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst", "manifest.progress")); !os.IsNotExist(err) {
t.Fatalf("expected progress state to be removed, stat err=%v", err)
}
}
Expand Down Expand Up @@ -875,7 +875,7 @@ func TestRunCLIStartDiscardSkipsCompletedMetadataRefresh(t *testing.T) {
if code != 0 {
t.Fatalf("start --discard completed refresh: expected 0, got %d stderr=%s", code, stderr.String())
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "manifest.progress")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst", "manifest.progress")); !os.IsNotExist(err) {
t.Fatalf("expected progress state to be removed, stat err=%v", err)
}
if _, err := os.Stat(filepath.Join(targetDir, "a.txt")); !os.IsNotExist(err) {
Expand Down Expand Up @@ -1624,7 +1624,7 @@ func TestRunCLISyncNoOpSkipsPrompt(t *testing.T) {
entry := buildTestManifestEntry(1, info.Size(), info.ModTime().UnixNano(), info.Mode(), "same.txt")
dirEntry := buildTestDirManifestEntry(0, dirMtime.UnixNano(), 0o750, "sub")
manifestRaw := buildTestManifestRaw("txsyncnoop", []string{dirEntry, entry})
if err := os.WriteFile(filepath.Join(tmp, ".pinch", "manifest.server"), []byte(manifestRaw), 0o644); err != nil {
if err := os.WriteFile(filepath.Join(tmp, ".pinch", "dst", "manifest.server"), []byte(manifestRaw), 0o644); err != nil {
t.Fatalf("write manifest.server: %v", err)
}
withSyncPromptTestInput(t, "\n", true)
Expand Down Expand Up @@ -2111,7 +2111,7 @@ func TestRunCLICopyStartPath(t *testing.T) {
if string(got) != string(payload) {
t.Fatalf("unexpected copied file: %q", got)
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst")); !os.IsNotExist(err) {
t.Fatalf("expected copy to remove state dir, stat err=%v", err)
}
}
Expand Down Expand Up @@ -2168,7 +2168,7 @@ func TestRunCLICopySyncPath(t *testing.T) {
if string(got) != string(payload) {
t.Fatalf("unexpected synced file: %q", got)
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst")); !os.IsNotExist(err) {
t.Fatalf("expected copy to remove state dir, stat err=%v", err)
}
}
Expand Down Expand Up @@ -2228,7 +2228,7 @@ func TestRunCLICopySkipFetchVerifyMeta(t *testing.T) {
if !strings.Contains(stdout.String(), "copy-verify-meta: ok total=2 files=1 hardlinks=0 symlinks=0 dirs=1") {
t.Fatalf("expected verify output, got stdout=%s stderr=%s", stdout.String(), stderr.String())
}
if _, err := os.Stat(filepath.Join(tmp, ".pinch")); err != nil {
if _, err := os.Stat(filepath.Join(tmp, ".pinch", "dst")); err != nil {
t.Fatalf("expected skip-fetch copy to preserve state dir, stat err=%v", err)
}
}
Expand Down
Loading