66 "fmt"
77 "io"
88 "os"
9+ "os/exec"
910 "path"
1011 "path/filepath"
1112 "strconv"
@@ -338,6 +339,24 @@ func ensureMountPoint(path string, mounter mount.Interface) (bool, error) {
338339 return isMnt , err
339340}
340341
342+ // ensureBindMountPoint evaluates whether a path is a valid mount point for bind mount.
343+ // In case the path does not exist, it will create a regular file for bind mount.
344+ // In case where the mount point exists, the mount point will be cleared and replaced by a new regular file.
345+ func ensureBindMountPoint (path string , mounter mount.Interface ) error {
346+ logrus .Infof ("Trying to ensure bind mount point %v" , path )
347+
348+ // blindly clean up the target mount point for recreation
349+ if cleanupErr := unmountAndCleanupMountPoint (path , mounter ); cleanupErr != nil {
350+ return errors .Wrapf (cleanupErr , "failed to remove corrupt bind mount point at %s" , path )
351+ }
352+
353+ // create regular file for bind mount
354+ if makeFileErr := makeFile (path ); makeFileErr != nil {
355+ return errors .Wrapf (makeFileErr , "failed to create regular file for bind mount at %s" , path )
356+ }
357+ return nil
358+ }
359+
341360// ensureDirectory checks if a folder exists at the specified path.
342361// If not, it creates the folder and returns true, otherwise returns false.
343362// If the path exists but is not a folder, it returns an error.
@@ -383,7 +402,16 @@ func unmount(path string, mounter mount.Interface) (err error) {
383402 return err
384403}
385404
386- // unmountAndCleanupMountPoint ensures all mount layers for the path are unmounted and the mount directory is removed
405+ func lazyUnmount (path string ) error {
406+ command := exec .Command ("umount" , "-l" , path )
407+ output , err := command .CombinedOutput ()
408+ if err != nil {
409+ return errors .Wrapf (err , "failed to lazy unmount a mount point %v, output: %s" , path , output )
410+ }
411+ return nil
412+ }
413+
414+ // unmountAndCleanupMountPoint ensures all mount layers for the path are unmounted and the mount point is removed
387415func unmountAndCleanupMountPoint (path string , mounter mount.Interface ) error {
388416 // we just try to unmount since the path check would get stuck for nfs mounts
389417 logrus .Infof ("Trying to umount mount point %v" , path )
@@ -392,8 +420,38 @@ func unmountAndCleanupMountPoint(path string, mounter mount.Interface) error {
392420 return err
393421 }
394422
423+ isMnt , validateUnmountErr := mounter .IsMountPoint (path )
424+ if validateUnmountErr != nil {
425+ if os .IsNotExist (validateUnmountErr ) {
426+ isMnt = false
427+ } else {
428+ return errors .Wrapf (validateUnmountErr , "failed to validate unmounted mount point %v" , path )
429+ }
430+ }
431+ if isMnt {
432+ logrus .Warnf ("Mount point %v is still busy after unmount, trying to lazy unmount to release mount point" , path )
433+ if lazyUnmountErr := lazyUnmount (path ); lazyUnmountErr != nil {
434+ return errors .Wrapf (lazyUnmountErr , "failed to to release mount point %v" , path )
435+ }
436+ }
437+
395438 logrus .Infof ("Trying to clean up mount point %v" , path )
396- return mount .CleanupMountPoint (path , mounter , true )
439+ if err := mount .CleanupMountPoint (path , mounter , true ); err != nil {
440+ return err
441+ }
442+ // ensure the file is removed even when it is a broken symbolic link
443+ info , statErr := os .Lstat (path )
444+ if statErr != nil {
445+ if os .IsNotExist (statErr ) {
446+ return nil
447+ }
448+ return errors .Wrapf (statErr , "failed to check cleaned up mount point %v" , path )
449+ }
450+ logrus .WithField ("fileMode" , info .Mode ().String ()).Infof ("Mount point %v exists after mount-utils clean up, removing the file" , path )
451+ if rmErr := os .Remove (path ); rmErr != nil {
452+ return errors .Wrapf (rmErr , "failed to remove broken symbolic link mount point %v" , path )
453+ }
454+ return nil
397455}
398456
399457// isBlockDevice return true if volumePath file is a block device, false otherwise.
@@ -439,7 +497,7 @@ func getFilesystemStatistics(volumePath string) (*volumeFilesystemStatistics, er
439497 return volStats , nil
440498}
441499
442- // makeFile creates an empty file.
500+ // makeFile creates an empty regular file.
443501// If pathname already exists, whether a file or directory, no error is returned.
444502func makeFile (pathname string ) error {
445503 f , err := os .OpenFile (pathname , os .O_CREATE , os .FileMode (0644 ))
0 commit comments