Skip to content

Commit 8a177de

Browse files
committed
Prefer fallocate but fall back to dd
1 parent 96ec53e commit 8a177de

File tree

1 file changed

+44
-9
lines changed

1 file changed

+44
-9
lines changed

manager/manager.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package manager
22

33
import (
4+
"fmt"
45
"github.com/pkg/errors"
56
"io/ioutil"
67
"os"
78
"os/exec"
89
"path/filepath"
910
"regexp"
11+
"strings"
1012
"syscall"
1113
)
1214

@@ -144,7 +146,7 @@ func (m Manager) Get(name string) (vol Volume, err error) {
144146
}
145147

146148

147-
func (m Manager) Create(name string, sizeInBytes int64, uid, gid int, mode uint32) error {
149+
func (m Manager) Create(name string, sizeInBytes int64, sparse bool, uid, gid int, mode uint32) error {
148150
err := validateName(name)
149151
if err != nil {
150152
return errors.Wrapf(err,
@@ -177,17 +179,50 @@ func (m Manager) Create(name string, sizeInBytes int64, uid, gid int, mode uint3
177179
name, dataFilePath)
178180
}
179181

180-
err = dataFileInfo.Truncate(sizeInBytes)
181-
if err != nil {
182-
_ = os.Remove(dataFilePath) // attempt to cleanup
183-
return errors.Wrapf(err,
184-
"Error creating volume '%s' - cannot allocate '%s' bytes",
185-
name, sizeInBytes)
182+
if sparse {
183+
err = dataFileInfo.Truncate(sizeInBytes)
184+
if err != nil {
185+
_ = os.Remove(dataFilePath) // attempt to cleanup
186+
return errors.Wrapf(err,
187+
"Error creating volume '%s' - cannot allocate '%s' bytes",
188+
name, sizeInBytes)
189+
}
190+
} else {
191+
// Try using fallocate - super fast if data dir is on ext4 or xfs
192+
errBytes, err := exec.Command("fallocate", "-l", fmt.Sprint(sizeInBytes), dataFilePath).CombinedOutput()
193+
194+
// fallocate failed - either not enough space or unsupported FS
195+
if err != nil {
196+
errStr := strings.TrimSpace(string(errBytes[:]))
197+
198+
// If there is not enough space then we just error out
199+
if strings.Contains(errStr, "No space") {
200+
_ = os.Remove(dataFilePath) // Primitive attempt to cleanup
201+
return errors.Wrapf(err,
202+
"Error creating volume '%s' - not enough disk space: '%s'", name, errStr)
203+
}
204+
205+
// Here we assume that FS is unsupported and will fall back to 'dd' which is slow but should work everywhere
206+
of := fmt.Sprintf("of=%s", dataFilePath)
207+
bs := int64(1000000)
208+
count := sizeInBytes / bs // we lose some precision here but it's likely to be negligible
209+
errBytes, err = exec.Command(
210+
"dd",
211+
"if=/dev/zero", of, fmt.Sprintf("bs=%d", bs), fmt.Sprintf("count=%d", count),
212+
).CombinedOutput()
213+
214+
// Something went wrong - likely no space on an fallocate-incompatible FS
215+
if err != nil {
216+
errStr = strings.TrimSpace(string(errBytes[:]))
217+
_ = os.Remove(dataFilePath) // Primitive attempt to cleanup
218+
return errors.Wrapf(err,
219+
"Error creating volume '%s' - '%s'", name, errStr)
220+
}
221+
}
186222
}
187223

188224
// format data file
189-
mkfsCmd := exec.Command("mkfs.ext4", "-F", dataFilePath)
190-
_, err = mkfsCmd.Output()
225+
_, err = exec.Command("mkfs.ext4", "-F", dataFilePath).Output()
191226
if err != nil {
192227
_ = os.Remove(dataFilePath) // attempt to cleanup
193228
return errors.Wrapf(err,

0 commit comments

Comments
 (0)