Finalize checks that the reconstructed file is complete, truncating the file to the correct length and verifying the checksum if it was provided. It returns the name of the file with the target file data, and any error encountered in verification; the target file data is complete and correct (to the
()
| 261 | // After this call, it is no longer valid to call other methods on |
| 262 | // the Syncer except for TargetFilename. |
| 263 | func (zs *Syncer) Finalize() (string, error) { |
| 264 | if zs.rs.BlocksTodo() > 0 { |
| 265 | return zs.curFilename, fmt.Errorf("file is not complete") |
| 266 | } |
| 267 | zs.rs = nil |
| 268 | if err := zs.tempFile.Truncate(zs.filelen); err != nil { |
| 269 | return zs.curFilename, fmt.Errorf("failed to truncate file: %w", err) |
| 270 | } |
| 271 | if _, err := zs.tempFile.Seek(0, io.SeekStart); err != nil { |
| 272 | return zs.curFilename, fmt.Errorf("failed to verify target file: %w", err) |
| 273 | } |
| 274 | |
| 275 | if zs.checksum != "" && zs.checksumMethod == "SHA-1" { |
| 276 | h := sha1.New() |
| 277 | if _, err := io.Copy(h, zs.tempFile); err != nil { |
| 278 | return zs.curFilename, fmt.Errorf("failed to read file: %w", err) |
| 279 | } |
| 280 | digest := hex.EncodeToString(h.Sum(nil)) |
| 281 | if !strings.EqualFold(digest, zs.checksum) { |
| 282 | return zs.curFilename, fmt.Errorf("checksum mismatch") |
| 283 | } |
| 284 | } |
| 285 | if zs.targetFilename != "" { |
| 286 | if err := os.Rename(zs.curFilename, zs.targetFilename); err != nil { |
| 287 | return zs.curFilename, fmt.Errorf("unable to move %s to %s: %v", zs.curFilename, zs.targetFilename, err) |
| 288 | } |
| 289 | zs.curFilename = zs.targetFilename |
| 290 | } |
| 291 | |
| 292 | err := zs.tempFile.Close() |
| 293 | if !zs.mtime.IsZero() { |
| 294 | if mtimeErr := os.Chtimes(zs.curFilename, time.Now(), zs.mtime); err != nil { |
| 295 | fmt.Fprintf(os.Stderr, "warning: failed to set mtime: %v\n", mtimeErr) |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | return zs.curFilename, err |
| 300 | } |