Skip to content

Commit 083b8fc

Browse files
committed
Move loop device creation to CreateVolume
This creates the loop device at CreateVolume and uses the loop device name as the volume ID. Also, replaces dd with fallocate to create block files.
1 parent fe5f9d8 commit 083b8fc

File tree

3 files changed

+74
-40
lines changed

3 files changed

+74
-40
lines changed

pkg/hostpath/controllerserver.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"fmt"
2121
"math"
2222
"os"
23+
"path/filepath"
2324
"sort"
2425
"strconv"
26+
"strings"
2527

2628
"github.com/golang/protobuf/ptypes"
2729

@@ -133,21 +135,38 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
133135
if capacity >= maxStorageCapacity {
134136
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, maxStorageCapacity)
135137
}
136-
volumeID := uuid.NewUUID().String()
137-
path := provisionRoot + volumeID
138+
139+
var volumeID, path string
138140

139141
switch requestedAccessType {
140142
case blockAccess:
143+
// Get a free loop device.
141144
executor := utilexec.New()
142-
of := fmt.Sprintf("%s=%s", "of", path)
143-
count := fmt.Sprintf("%s=%d", "count", capacity/mib)
145+
out, err := executor.Command("losetup", "-f").CombinedOutput()
146+
if err != nil {
147+
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get a free loop device: %v: %s", err, out))
148+
}
149+
loopDevice := strings.TrimSpace(string(out))
150+
151+
// Use the loop device name as the volume ID.
152+
volumeID = filepath.Base(loopDevice)
153+
path = provisionRoot + volumeID
154+
size := fmt.Sprintf("%dM", capacity/mib)
144155
// Create a block file.
145-
out, err := executor.Command("dd", "if=/dev/zero", of, "bs=1M", count).CombinedOutput()
156+
out, err = executor.Command("fallocate", "-l", size, path).CombinedOutput()
146157
if err != nil {
147158
glog.V(3).Infof("failed to create block device: %v", string(out))
148159
return nil, err
149160
}
161+
162+
// Associate block file with the loop device.
163+
out, err = executor.Command("losetup", loopDevice, path).CombinedOutput()
164+
if err != nil {
165+
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to associate loop device with block file: %v: %s", err, out))
166+
}
150167
case mountAccess:
168+
volumeID = uuid.NewUUID().String()
169+
path = provisionRoot + volumeID
151170
err := os.MkdirAll(path, 0777)
152171
if err != nil {
153172
glog.V(3).Infof("failed to create volume: %v", err)
@@ -205,6 +224,21 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
205224
}
206225
volumeID := req.VolumeId
207226
glog.V(4).Infof("deleting volume %s", volumeID)
227+
228+
// Create the loop device path using volume ID.
229+
loopDevicePath := filepath.Join("/dev", volumeID)
230+
// Disassociate the loop device.
231+
executor := utilexec.New()
232+
out, err := executor.Command("losetup", "-d", loopDevicePath).CombinedOutput()
233+
if err != nil {
234+
if strings.Contains(string(out), "No such device or address") {
235+
// The loop device is no longer associated. Do nothing.
236+
} else {
237+
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to disassociate loop device: %v: %s", err, out))
238+
}
239+
}
240+
241+
// Delete the block file.
208242
path := provisionRoot + volumeID
209243
os.RemoveAll(path)
210244
delete(hostPathVolumes, volumeID)

pkg/hostpath/hostpath.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ type hostPathVolume struct {
5050
VolSize int64 `json:"volSize"`
5151
VolPath string `json:"volPath"`
5252
VolAccessType accessType `json:"volAccessType"`
53-
VolLoopDevice string `json:"volLoopDevice"`
5453
}
5554

5655
type hostPathSnapshot struct {

pkg/hostpath/nodeserver.go

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package hostpath
1919
import (
2020
"fmt"
2121
"os"
22-
"strings"
22+
"path/filepath"
2323

2424
"github.com/golang/glog"
2525
"golang.org/x/net/context"
@@ -71,36 +71,45 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
7171
return nil, status.Error(codes.InvalidArgument, "cannot publish a non-block volume as block volume")
7272
}
7373

74+
// Create the loop device path using volume ID.
75+
loopDevice := filepath.Join("/dev", vol.VolID)
76+
7477
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))
8178

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
8588
}
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()
9189
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)
9391
}
9492

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+
}
10099

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+
}
104113
} else if req.GetVolumeCapability().GetMount() != nil {
105114
if vol.VolAccessType != mountAccess {
106115
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
171180
switch vol.VolAccessType {
172181
case blockAccess:
173182
// 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)
179184
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())
181186
}
182-
183-
// Update the volume info.
184-
vol.VolLoopDevice = ""
185-
hostPathVolumes[vol.VolID] = vol
186187
glog.V(4).Infof("hostpath: volume %s has been unpublished.", targetPath)
187188
case mountAccess:
188189
// Unmounting the image

0 commit comments

Comments
 (0)