@@ -19,7 +19,7 @@ package hostpath
19
19
import (
20
20
"fmt"
21
21
"os"
22
- "strings "
22
+ "path/filepath "
23
23
24
24
"github.com/golang/glog"
25
25
"golang.org/x/net/context"
@@ -71,36 +71,45 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
71
71
return nil , status .Error (codes .InvalidArgument , "cannot publish a non-block volume as block volume" )
72
72
}
73
73
74
+ // Create the loop device path using volume ID.
75
+ loopDevice := filepath .Join ("/dev" , vol .VolID )
76
+
74
77
executor := utilexec .New ()
75
- // Get a free loop device.
76
- out , err := executor .Command ("losetup" , "-f" ).CombinedOutput ()
77
- if err != nil {
78
- return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to get a free loop device: %v: %s" , err , out ))
79
- }
80
- loopDevice := strings .TrimSpace (string (out ))
81
78
82
- losetupArgs := []string {}
83
- if req .GetReadonly () {
84
- losetupArgs = append (losetupArgs , "-r" )
79
+ // Get FileInfo to check if the file already exists.
80
+ fi , err := os .Lstat (targetPath )
81
+ if os .IsNotExist (err ) {
82
+ // The target path does not exists. Create a symlink.
83
+ out , err := executor .Command ("ln" , "-s" , loopDevice , targetPath ).CombinedOutput ()
84
+ if err != nil {
85
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to create symlink to target path: %v: %s" , err , out ))
86
+ }
87
+ return & csi.NodePublishVolumeResponse {}, nil
85
88
}
86
- // Append loop device and file path.
87
- losetupArgs = append (losetupArgs , loopDevice , vol .VolPath )
88
-
89
- // Associate block file with the loop device.
90
- out , err = executor .Command ("losetup" , losetupArgs ... ).CombinedOutput ()
91
89
if err != nil {
92
- return nil , status .Error (codes .Internal , fmt . Sprintf ( "failed to associate loop device with block file : %v: %s " , err , out ) )
90
+ return nil , status .Errorf (codes .Internal , "failed to check if the target path exists : %v" , err )
93
91
}
94
92
95
- // Create symlink to the target path.
96
- out , err = executor .Command ("ln" , "-s" , loopDevice , targetPath ).CombinedOutput ()
97
- if err != nil {
98
- return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to create symlink to target path: %v: %s" , err , out ))
99
- }
93
+ // If it's a symlink, check if it resolves to the same loop device.
94
+ if fi .Mode ()& os .ModeSymlink != 0 {
95
+ link , err := os .Readlink (fi .Name ())
96
+ if err != nil {
97
+ return nil , status .Errorf (codes .Internal , "failed to resolve the target path symbolic link: %v" , err )
98
+ }
100
99
101
- // Update the volume info.
102
- vol .VolLoopDevice = loopDevice
103
- hostPathVolumes [vol .VolID ] = vol
100
+ if link != loopDevice {
101
+ return nil , status .Errorf (codes .AlreadyExists , "target path linked to another device(%s)" , link )
102
+ }
103
+ // Do nothing. Symlink already exists with the right loop device.
104
+ } else {
105
+ // A regular file exists. This could be a left over file that failed
106
+ // to delete while unpublishing. Reuse it by forcefully creating
107
+ // symlink to it.
108
+ out , err := executor .Command ("ln" , "-s" , "-f" , loopDevice , targetPath ).CombinedOutput ()
109
+ if err != nil {
110
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to forcefully create symlink to an existing target file: %v: %s" , err , out ))
111
+ }
112
+ }
104
113
} else if req .GetVolumeCapability ().GetMount () != nil {
105
114
if vol .VolAccessType != mountAccess {
106
115
return nil , status .Error (codes .InvalidArgument , "cannot publish a non-mount volume as mount volume" )
@@ -171,18 +180,10 @@ func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu
171
180
switch vol .VolAccessType {
172
181
case blockAccess :
173
182
// Remove the symlink.
174
- os .RemoveAll (targetPath )
175
-
176
- // Disassociate the loop device.
177
- executor := utilexec .New ()
178
- out , err := executor .Command ("losetup" , "-d" , vol .VolLoopDevice ).CombinedOutput ()
183
+ err = os .RemoveAll (targetPath )
179
184
if err != nil {
180
- return nil , status .Error (codes .Internal , fmt . Sprintf ( "failed to disassociate loop device: %v: %s" , err , out ))
185
+ return nil , status .Error (codes .Internal , err . Error ( ))
181
186
}
182
-
183
- // Update the volume info.
184
- vol .VolLoopDevice = ""
185
- hostPathVolumes [vol .VolID ] = vol
186
187
glog .V (4 ).Infof ("hostpath: volume %s has been unpublished." , targetPath )
187
188
case mountAccess :
188
189
// Unmounting the image
0 commit comments