|
1 | 1 | package manager
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
4 | 5 | "github.com/pkg/errors"
|
5 | 6 | "io/ioutil"
|
6 | 7 | "os"
|
7 | 8 | "os/exec"
|
8 | 9 | "path/filepath"
|
9 | 10 | "regexp"
|
| 11 | + "strings" |
10 | 12 | "syscall"
|
11 | 13 | )
|
12 | 14 |
|
@@ -144,7 +146,7 @@ func (m Manager) Get(name string) (vol Volume, err error) {
|
144 | 146 | }
|
145 | 147 |
|
146 | 148 |
|
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 { |
148 | 150 | err := validateName(name)
|
149 | 151 | if err != nil {
|
150 | 152 | return errors.Wrapf(err,
|
@@ -177,17 +179,50 @@ func (m Manager) Create(name string, sizeInBytes int64, uid, gid int, mode uint3
|
177 | 179 | name, dataFilePath)
|
178 | 180 | }
|
179 | 181 |
|
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 | + } |
186 | 222 | }
|
187 | 223 |
|
188 | 224 | // format data file
|
189 |
| - mkfsCmd := exec.Command("mkfs.ext4", "-F", dataFilePath) |
190 |
| - _, err = mkfsCmd.Output() |
| 225 | + _, err = exec.Command("mkfs.ext4", "-F", dataFilePath).Output() |
191 | 226 | if err != nil {
|
192 | 227 | _ = os.Remove(dataFilePath) // attempt to cleanup
|
193 | 228 | return errors.Wrapf(err,
|
|
0 commit comments