Skip to content

Commit 96ec53e

Browse files
committed
Add options for adjusting volume credentials: uid/gid/mode
1 parent 7a37417 commit 96ec53e

File tree

3 files changed

+127
-18
lines changed

3 files changed

+127
-18
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
https://serverfault.com/questions/166748/performance-of-loopback-filesystems
33
https://kernelnewbies.org/Linux_4.4#Faster_and_leaner_loop_device_with_Direct_I.2FO_and_Asynchronous_I.2FO_support
44

5-
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bc07c10a3603a5ab3ef01ba42b3d41f9ac63d1b6
5+
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bc07c10a3603a5ab3ef01ba42b3d41f9ac63d1b6
6+
7+
```
8+
chown -1:-1
9+
10+
```

driver/driver.go

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package driver
33
import (
44
"fmt"
55
"os"
6+
"strconv"
67
"sync"
78
"time"
8-
"strconv"
99

1010
"github.com/ashald/docker-volume-loopback/manager"
1111
"github.com/pkg/errors"
@@ -64,9 +64,9 @@ func (d Driver) Create(req *v.CreateRequest) error {
6464
Str("opts-size", req.Options["size"]).
6565
Logger()
6666

67-
size, present := req.Options["size"]
67+
size, sizePresent := req.Options["size"]
6868

69-
if !present {
69+
if !sizePresent {
7070
logger.Debug().
7171
Str("default", d.defaultSize).
7272
Msg("no size opt found, using default")
@@ -80,12 +80,68 @@ func (d Driver) Create(req *v.CreateRequest) error {
8080
size)
8181
}
8282

83+
uid := -1
84+
uidStr, uidPresent := req.Options["uid"]
85+
if uidPresent && len(uidStr) > 0 {
86+
uid, err = strconv.Atoi(uidStr)
87+
if err != nil {
88+
return errors.Wrapf(err,
89+
"Error creating volume '%s' - cannot parse 'uid' option '%s' as an integer",
90+
req.Name, uidStr)
91+
}
92+
if uid < 0 {
93+
return errors.Errorf(
94+
"Error creating volume '%s' - 'uid' option should be >= 0 but received '%s'",
95+
req.Name, uid)
96+
}
97+
98+
logger.Debug().
99+
Int("uid", uid).
100+
Msg("set volume root uid")
101+
}
102+
103+
gid := -1
104+
gidStr, gidPresent := req.Options["gid"]
105+
if gidPresent && len(gidStr) > 0 {
106+
gid, err = strconv.Atoi(gidStr)
107+
if err != nil {
108+
return errors.Wrapf(err,
109+
"Error creating volume '%s' - cannot parse 'gid' option '%s' as an integer",
110+
req.Name, gidStr)
111+
}
112+
if gid < 0 {
113+
return errors.Errorf(
114+
"Error creating volume '%s' - 'gid' option should be >= 0 but received '%s'",
115+
req.Name, gid)
116+
}
117+
118+
logger.Debug().
119+
Int("gid", gid).
120+
Msg("set volume root gid")
121+
}
122+
123+
var mode uint32
124+
modeStr, modePresent := req.Options["mode"]
125+
if modePresent && len(modeStr) > 0 {
126+
logger.Debug().
127+
Str("mode", modeStr).
128+
Msg("will parse as octal")
129+
130+
modeParsed, err := strconv.ParseUint(modeStr, 8, 32)
131+
if err != nil || modeParsed <= 0 || modeParsed > 07777 {
132+
return errors.Wrapf(err,
133+
"Error creating volume '%s' - cannot parse mode '%s' as 4-position octal",
134+
req.Name, modeStr)
135+
}
136+
mode = uint32(modeParsed)
137+
}
138+
83139
d.Lock()
84140
defer d.Unlock()
85141

86142
logger.Debug().Msg("starting creation")
87143

88-
err = d.manager.Create(req.Name, sizeInBytes)
144+
err = d.manager.Create(req.Name, sizeInBytes, uid, gid, mode)
89145
if err != nil {
90146
return err
91147
}
@@ -226,7 +282,7 @@ func (d Driver) Mount(req *v.MountRequest) (*v.MountResponse, error) {
226282
logger.Debug().Msg("finished mounting volume")
227283

228284
resp := new(v.MountResponse)
229-
resp.Mountpoint = *entrypoint
285+
resp.Mountpoint = entrypoint
230286
return resp, nil
231287
}
232288

manager/manager.go

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (m Manager) Get(name string) (vol Volume, err error) {
144144
}
145145

146146

147-
func (m Manager) Create(name string, sizeInBytes int64) error {
147+
func (m Manager) Create(name string, sizeInBytes int64, uid, gid int, mode uint32) error {
148148
err := validateName(name)
149149
if err != nil {
150150
return errors.Wrapf(err,
@@ -166,17 +166,20 @@ func (m Manager) Create(name string, sizeInBytes int64) error {
166166

167167
}
168168

169-
// create vessel
169+
// create data file
170170
dataFilePath := filepath.Join(m.dataDir, name)
171171
dataFileInfo, err := os.Create(dataFilePath)
172172
if err != nil {
173+
_ = os.Remove(dataFilePath) // attempt to cleanup
174+
173175
return errors.Wrapf(err,
174176
"Error creating volume '%s' - cannot create datafile '%s'",
175177
name, dataFilePath)
176178
}
177179

178180
err = dataFileInfo.Truncate(sizeInBytes)
179181
if err != nil {
182+
_ = os.Remove(dataFilePath) // attempt to cleanup
180183
return errors.Wrapf(err,
181184
"Error creating volume '%s' - cannot allocate '%s' bytes",
182185
name, sizeInBytes)
@@ -186,30 +189,73 @@ func (m Manager) Create(name string, sizeInBytes int64) error {
186189
mkfsCmd := exec.Command("mkfs.ext4", "-F", dataFilePath)
187190
_, err = mkfsCmd.Output()
188191
if err != nil {
192+
_ = os.Remove(dataFilePath) // attempt to cleanup
189193
return errors.Wrapf(err,
190194
"Error creating volume '%s' - cannot format datafile as ext4 filesystem",
191195
name, sizeInBytes)
192196
}
193197

198+
// At this point we're done - last step is to adjust ownership if required.
199+
if uid >= 0 || gid >= 0 {
200+
lease := "driver"
201+
202+
mountPath, err := m.Mount(name, lease)
203+
if err != nil {
204+
_ = os.Remove(dataFilePath) // attempt to cleanup
205+
return errors.Wrapf(err,
206+
"Error creating volume '%s' - cannot mount volume to adjust its root owner/permissions",
207+
name, sizeInBytes)
208+
}
209+
210+
if mode > 0 {
211+
err = os.Chmod(mountPath, os.FileMode(mode))
212+
if err != nil {
213+
_ = m.UnMount(name, lease)
214+
_ = os.Remove(dataFilePath) // attempt to cleanup
215+
return errors.Wrapf(err,
216+
"Error creating volume '%s' - cannot adjust volume root permissions",
217+
name, sizeInBytes)
218+
}
219+
}
220+
err = os.Chown(mountPath, uid, gid)
221+
if err != nil {
222+
_ = m.UnMount(name, lease)
223+
_ = os.Remove(dataFilePath) // attempt to cleanup
224+
return errors.Wrapf(err,
225+
"Error creating volume '%s' - cannot adjust volume root owner",
226+
name, sizeInBytes)
227+
}
228+
229+
err = m.UnMount(name, lease)
230+
if err != nil {
231+
_ = os.Remove(dataFilePath) // attempt to cleanup
232+
return errors.Wrapf(err,
233+
"Error creating volume '%s' - cannot unmount volume after adjusting its root owner/permissions",
234+
name, sizeInBytes)
235+
}
236+
}
237+
194238
return nil
195239
}
196240

197-
func (m Manager) Mount(name string, lease string) (*string, error) {
241+
func (m Manager) Mount(name string, lease string) (string, error) {
242+
var failedResult string
243+
198244
err := validateName(name)
199245
if err != nil {
200-
return nil, errors.Wrapf(err,
246+
return failedResult, errors.Wrapf(err,
201247
"Error mounting volume '%s' - invalid volume name",
202248
name)
203249
}
204250

205251
vol, err := m.getVolume(name)
206252
if err != nil {
207-
return nil, errors.Wrapf(err, "Error mounting volume '%s' - cannot get its metadata", name)
253+
return failedResult, errors.Wrapf(err, "Error mounting volume '%s' - cannot get its metadata", name)
208254
}
209255

210256
isAlreadyMounted, err := vol.IsMounted() // checking mount status early before we record a lease
211257
if err != nil {
212-
return nil, errors.Wrapf(err, "Error mounting volume '%s' - cannot check its mount status", name)
258+
return failedResult, errors.Wrapf(err, "Error mounting volume '%s' - cannot check its mount status", name)
213259
}
214260

215261
_, err = os.Stat(vol.StateDir)
@@ -218,7 +264,7 @@ func (m Manager) Mount(name string, lease string) (*string, error) {
218264
if os.IsNotExist(err) {
219265
err = os.MkdirAll(vol.StateDir, 0755)
220266
if err != nil {
221-
return nil, errors.Wrapf(err,
267+
return failedResult, errors.Wrapf(err,
222268
"Error mounting volume '%s' - cannot create its state dir",
223269
name)
224270
}
@@ -229,34 +275,36 @@ func (m Manager) Mount(name string, lease string) (*string, error) {
229275
_, err = os.Stat(leaseFile)
230276
if err != nil {
231277
if !os.IsNotExist(err) {
232-
return nil, errors.Wrapf(err,
278+
return failedResult, errors.Wrapf(err,
233279
"Error mounting volume '%s' - cannot access lease file '%s'",
234280
name, leaseFile)
235281
}
236282
}
237283
_, err = os.Create(leaseFile)
238284
if err != nil {
239-
return nil, errors.Wrapf(err,
285+
return failedResult, errors.Wrapf(err,
240286
"Error mounting volume '%s' - cannot create lease file '%s'",
241287
name, lease)
242288
}
243289

244290
if !isAlreadyMounted {
245291
err = os.Mkdir(vol.MountPointPath, 0777)
246292
if err != nil {
247-
return nil, errors.Wrapf(err,
293+
_ = os.Remove(leaseFile) // attempt to cleanup
294+
return failedResult, errors.Wrapf(err,
248295
"Error mounting volume '%s' - cannot create mount point dir",
249296
name)
250297
}
251298
mountCmd := exec.Command("mount", vol.DataFilePath, vol.MountPointPath)
252299
_, err = mountCmd.Output()
253300
if err != nil {
254-
return nil, errors.Wrapf(err,
301+
_ = os.Remove(leaseFile) // attempt to cleanup
302+
return failedResult, errors.Wrapf(err,
255303
"Error mounting volume '%s' - cannot mount vessel '%s' at '%s'",
256304
name, vol.DataFilePath, vol.MountPointPath)
257305
}
258306
}
259-
return &vol.MountPointPath, nil
307+
return vol.MountPointPath, nil
260308
}
261309

262310
func (m Manager) UnMount(name string, lease string) error {

0 commit comments

Comments
 (0)