@@ -348,15 +348,12 @@ func (sc *Controller) upgradeDaemons() func(w http.ResponseWriter, r *http.Reque
348348 sourcePath := c .NydusdPath
349349 destinationPath := manager .NydusdBinaryPath
350350
351- if err = copyNydusdBinary (sourcePath , destinationPath ); err != nil {
351+ if err = upgradeNydusdWithSymlink (sourcePath , destinationPath ); err != nil {
352352 log .L .Errorf ("Failed to copy nydusd binary from %s to %s: %v" ,
353353 sourcePath , destinationPath , err )
354354 statusCode = http .StatusInternalServerError
355355 return
356356 }
357-
358- log .L .Infof ("Successfully copy nydusd binary from %s to %s" ,
359- sourcePath , destinationPath )
360357 }
361358 }
362359}
@@ -470,36 +467,51 @@ func buildNextAPISocket(cur string) (string, error) {
470467 return nextSocket , nil
471468}
472469
473- // copyNydusdBinary copies a file from sourcePath to destinationPath,
474- // ensuring parent directories exist and setting 0755 permissions.
475- // It overwrites the destination file if it already exists.
476- func copyNydusdBinary (sourcePath , destinationPath string ) error {
477- fileMode := os .FileMode (0755 )
470+ // upgradeNydusdWithSymlink atomically creates a symbolic link from destinationPath to sourcePath.
471+ // It uses atomic rename to avoid any gap period where the destination doesn't exist.
472+ // Running processes are not affected as they hold file descriptors to the original inode.
473+ func upgradeNydusdWithSymlink (sourcePath , destinationPath string ) error {
474+ // Ensure source file exists and is accessible
475+ if _ , err := os .Stat (sourcePath ); err != nil {
476+ return fmt .Errorf ("source file %s does not exist or is not accessible: %w" , sourcePath , err )
477+ }
478478
479+ // Ensure destination directory exists
479480 destDir := filepath .Dir (destinationPath )
480- if err := os .MkdirAll (destDir , fileMode ); err != nil {
481+ if err := os .MkdirAll (destDir , 0755 ); err != nil {
481482 return fmt .Errorf ("failed to create destination directory %s: %w" , destDir , err )
482483 }
483484
484- sourceFile , err := os .Open (sourcePath )
485+ // Use absolute path for source to avoid relative path issues
486+ absSourcePath , err := filepath .Abs (sourcePath )
485487 if err != nil {
486- return fmt .Errorf ("failed to open source file %s: %w" , sourcePath , err )
488+ return fmt .Errorf ("failed to get absolute path for %s: %w" , sourcePath , err )
487489 }
488- defer sourceFile .Close ()
489490
490- destinationFile , err := os .OpenFile (destinationPath , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , fileMode )
491+ // Get absolute path for destination to compare
492+ absDestinationPath , err := filepath .Abs (destinationPath )
491493 if err != nil {
492- return fmt .Errorf ("failed to create destination file %s: %w" , destinationPath , err )
494+ return fmt .Errorf ("failed to get absolute path for %s: %w" , destinationPath , err )
495+ }
496+
497+ // Check if source and destination are the same to avoid circular symlink
498+ if absSourcePath == absDestinationPath {
499+ return fmt .Errorf ("source path and destination path are the same: %s" , absSourcePath )
493500 }
494- defer destinationFile .Close ()
495501
496- if _ , err := io .Copy (destinationFile , sourceFile ); err != nil {
497- return fmt .Errorf ("failed to copy file contents from %s to %s: %w" , sourcePath , destinationPath , err )
502+ // Create a temporary symbolic link first
503+ tempSymlinkPath := absDestinationPath + ".tmp"
504+ if err := os .Symlink (absSourcePath , tempSymlinkPath ); err != nil {
505+ return fmt .Errorf ("failed to create temporary symbolic link %s: %w" , tempSymlinkPath , err )
498506 }
499507
500- if err := os .Chmod (destinationPath , fileMode ); err != nil {
501- return fmt .Errorf ("failed to set permissions for %s to %s: %w" , destinationPath , fileMode .String (), err )
508+ // Atomically replace the destination with the temporary symlink
509+ if err := os .Rename (tempSymlinkPath , absDestinationPath ); err != nil {
510+ // Clean up temporary symlink if rename fails
511+ os .Remove (tempSymlinkPath )
512+ return fmt .Errorf ("failed to atomically replace symlink %s: %w" , absDestinationPath , err )
502513 }
503514
515+ log .L .Infof ("Successfully created symbolic link from %s to %s" , absDestinationPath , absSourcePath )
504516 return nil
505517}
0 commit comments