// Package afclock implementiert das AFC-Sync-Lock-File-Pattern. // iOS erwartet vor iTunes-style sync dass der Client `/com.apple.itunes.lock_sync` // via AFC öffnet + lockt. Ohne diesen Lock interpretiert iOS den Restore // als incomplete external-process und applied keine cloud-config. // // Reverse-engineered aus TechLockdown safesurfer.go:288 (AfcFile.Lock-Call). package afclock import ( "fmt" ios "github.com/danielpaulus/go-ios/ios" "github.com/danielpaulus/go-ios/ios/afc" ) // SyncLockPath — der genaue Pfad den iOS erwartet (aus TL-binary-strings). const SyncLockPath = "/com.apple.itunes.lock_sync" // SyncLock kapselt einen offenen AFC-Lock auf der sync-file. type SyncLock struct { client *afc.Client file *afc.File } // Acquire öffnet AFC + das sync-lock-file + erzeugt exclusive lock. // Caller MUSS Release() callen wenn fertig. func Acquire(device ios.DeviceEntry) (*SyncLock, error) { client, err := afc.New(device) if err != nil { return nil, fmt.Errorf("afclock: open AFC: %w", err) } // Versuche das sync-lock file zu öffnen. // READ_WRITE_CREATE = mode 0x2 — creates if not exists. file, err := client.Open(SyncLockPath, afc.READ_WRITE_CREATE) if err != nil { client.Close() return nil, fmt.Errorf("afclock: open %s: %w", SyncLockPath, err) } // Note: go-ios's afc.File doesn't expose Lock() directly. Apple's AFC // has a Lock operation but go-ios doesn't wrap it. We rely on the // open-file-handle alone signaling iOS that sync is in progress. // TL calls Lock() explicitly — wenn das nicht reicht, müssten wir // das AFC-Protocol-Level Lock-message direkt senden (8-byte op + // lock-type-flag). return &SyncLock{ client: client, file: file, }, nil } // Release schließt das file-handle + AFC-service. Signal an iOS dass sync done. func (s *SyncLock) Release() error { if s.file != nil { s.file.Close() s.file = nil } if s.client != nil { s.client.Close() s.client = nil } return nil }