| 242 | } |
| 243 | |
| 244 | func copyFile(src, dst string) error { |
| 245 | // Validate inputs |
| 246 | if src == "" || dst == "" { |
| 247 | return errors.New("source or destination paths cannot be empty") |
| 248 | } |
| 249 | |
| 250 | // Check source file |
| 251 | sourceFileStat, err := os.Stat(src) |
| 252 | if err != nil { |
| 253 | return errors.Wrapf(err, "failed to stat source file: %s", src) |
| 254 | } |
| 255 | if !sourceFileStat.Mode().IsRegular() { |
| 256 | return errors.Errorf("%s is not a regular file", src) |
| 257 | } |
| 258 | |
| 259 | // Check if destination already exists and matches source size |
| 260 | if destStat, err := os.Stat(dst); err == nil { |
| 261 | if destStat.Size() == sourceFileStat.Size() { |
| 262 | log.Printf("[INFO] destination file %s already exists with matching size, skipping copy", dst) |
| 263 | return nil |
| 264 | } |
| 265 | log.Printf("[WARNING] destination file %s exists but size mismatch (source=%d, dest=%d), will overwrite", |
| 266 | dst, sourceFileStat.Size(), destStat.Size()) |
| 267 | } |
| 268 | |
| 269 | // Open source file |
| 270 | source, err := os.Open(src) |
| 271 | if err != nil { |
| 272 | return errors.Wrapf(err, "failed to open source file: %s", src) |
| 273 | } |
| 274 | defer source.Close() |
| 275 | |
| 276 | // Create destination directory if it doesn't exist |
| 277 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { |
| 278 | return errors.Wrapf(err, "failed to create destination directory: %s", filepath.Dir(dst)) |
| 279 | } |
| 280 | |
| 281 | // Create destination file |
| 282 | destination, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, sourceFileStat.Mode()) |
| 283 | if err != nil { |
| 284 | return errors.Wrapf(err, "failed to create destination file: %s", dst) |
| 285 | } |
| 286 | |
| 287 | // Copy the file |
| 288 | if _, err := io.Copy(destination, source); err != nil { |
| 289 | return errors.Wrap(err, "failed to copy file contents") |
| 290 | } |
| 291 | |
| 292 | // Ensure data is flushed to disk |
| 293 | if err := destination.Sync(); err != nil { |
| 294 | return errors.Wrap(err, "failed to sync destination file") |
| 295 | } |
| 296 | |
| 297 | // Close the destination file |
| 298 | if err := destination.Close(); err != nil { |
| 299 | return errors.Wrap(err, "failed to close destination file") |
| 300 | } |
| 301 | |