Skip to content

Commit 37aa025

Browse files
milantracygvisor-bot
authored andcommitted
Support nr_blocks mount option in tmpfs.
Refactors tmpfs mount option parsing to use this ordered parser. This aligns with linux "last one wins" behavior when conflicting options like "size" and "nr_blocks" are specified. PiperOrigin-RevId: 942353157
1 parent abf5927 commit 37aa025

4 files changed

Lines changed: 302 additions & 91 deletions

File tree

pkg/sentry/fsimpl/tmpfs/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ go_test(
172172
"//pkg/context",
173173
"//pkg/errors/linuxerr",
174174
"//pkg/fspath",
175+
"//pkg/hostarch",
175176
"//pkg/sentry/contexttest",
176177
"//pkg/sentry/fsimpl/lock",
177178
"//pkg/sentry/kernel/auth",

pkg/sentry/fsimpl/tmpfs/tmpfs.go

Lines changed: 110 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -256,109 +256,128 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
256256
rootMode = 01777
257257
}
258258

259-
mopts := vfs.GenericParseMountOptions(opts.Data)
260-
var printedOpts []string
261-
262259
maxSizeInPages := getDefaultSizeLimit(disableDefaultSizeLimit) / hostarch.PageSize
263-
maxSizeStr, ok := mopts["size"]
264-
if ok {
265-
delete(mopts, "size")
266-
maxSizeInBytes, _, err := parseSize(maxSizeStr)
267-
if err != nil {
268-
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: parseSize() failed: %v", err)
269-
return nil, nil, linuxerr.EINVAL
270-
}
260+
maxInodes := getDefaultInodeLimit(disableDefaultSizeLimit)
261+
rootKUID := creds.EffectiveKUID
262+
rootKGID := creds.EffectiveKGID
271263

272-
var printedSize uint64
273-
if maxSizeInBytes > 0 {
274-
// If size > 0, convert size in bytes to nearest Page Size
275-
// bytes as Linux allocates memory in terms of Page size.
276-
maxSizeInPages, ok = hostarch.ToPagesRoundUp(maxSizeInBytes)
277-
if !ok {
278-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: Pages RoundUp Overflow error: %v", ok)
264+
printedOptsMap := make(map[string]string)
265+
266+
for _, opt := range vfs.GenericParseMountOptionsOrdered(opts.Data) {
267+
key := opt.Key
268+
value := opt.Value
269+
270+
switch key {
271+
case "size":
272+
maxSizeInBytes, _, err := parseSize(value)
273+
if err != nil {
274+
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: parseSize() failed for size: %v", err)
279275
return nil, nil, linuxerr.EINVAL
280276
}
281-
printedSize = (maxSizeInPages*hostarch.PageSize + 1023) / 1024
282-
} else {
283-
// size = 0 is a special case: no size limit
284-
maxSizeInPages = math.MaxInt64
285-
printedSize = 0
286-
}
277+
var printedSize uint64
278+
if maxSizeInBytes > 0 {
279+
var ok bool
280+
maxSizeInPages, ok = hostarch.ToPagesRoundUp(maxSizeInBytes)
281+
if !ok {
282+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: Pages RoundUp Overflow error")
283+
return nil, nil, linuxerr.EINVAL
284+
}
285+
printedSize = (maxSizeInPages*hostarch.PageSize + 1023) / 1024
286+
} else {
287+
maxSizeInPages = math.MaxInt64
288+
printedSize = 0
289+
}
290+
printedOptsMap["size"] = fmt.Sprintf("size=%vk", printedSize)
287291

288-
printedOpts = append(printedOpts, fmt.Sprintf("size=%vk", printedSize))
289-
}
290-
maxInodes := getDefaultInodeLimit(disableDefaultSizeLimit)
291-
maxInodesStr, ok := mopts["nr_inodes"]
292-
if ok {
293-
delete(mopts, "nr_inodes")
294-
var err error
295-
var percentageSpecified bool
296-
maxInodes, percentageSpecified, err = parseSize(maxInodesStr)
297-
if percentageSpecified {
298-
// Percentage not allowed for specifying inode limit
299-
return nil, nil, linuxerr.EINVAL
300-
}
301-
if err != nil {
302-
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: parseSize() failed: %v", err)
303-
return nil, nil, linuxerr.EINVAL
304-
}
292+
case "nr_blocks":
293+
maxBlocks, percentageSpecified, err := parseSize(value)
294+
if percentageSpecified {
295+
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: percentage not allowed for nr_blocks")
296+
return nil, nil, linuxerr.EINVAL
297+
}
298+
if err != nil {
299+
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: parseSize() failed for nr_blocks: %v", err)
300+
return nil, nil, linuxerr.EINVAL
301+
}
302+
maxSizeInPages = maxBlocks
303+
printedSize := (maxSizeInPages*hostarch.PageSize + 1023) / 1024
304+
printedOptsMap["size"] = fmt.Sprintf("size=%vk", printedSize)
305+
306+
case "nr_inodes":
307+
var err error
308+
var percentageSpecified bool
309+
maxInodes, percentageSpecified, err = parseSize(value)
310+
if percentageSpecified {
311+
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: percentage not allowed for nr_inodes")
312+
return nil, nil, linuxerr.EINVAL
313+
}
314+
if err != nil {
315+
ctx.Debugf("tmpfs.FilesystemType.GetFilesystem: parseSize() failed for nr_inodes: %v", err)
316+
return nil, nil, linuxerr.EINVAL
317+
}
318+
printedOptsMap["nr_inodes"] = fmt.Sprintf("nr_inodes=%v", maxInodes)
305319

306-
printedOpts = append(printedOpts, fmt.Sprintf("nr_inodes=%v", maxInodes))
307-
}
308-
modeStr, ok := mopts["mode"]
309-
if ok {
310-
delete(mopts, "mode")
311-
mode, err := strconv.ParseUint(modeStr, 8, 32)
312-
if err != nil {
313-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid mode: %q", modeStr)
314-
return nil, nil, linuxerr.EINVAL
315-
}
316-
rootMode = linux.FileMode(mode & 07777)
320+
case "mode":
321+
mode, err := strconv.ParseUint(value, 8, 32)
322+
if err != nil {
323+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid mode: %q", value)
324+
return nil, nil, linuxerr.EINVAL
325+
}
326+
rootMode = linux.FileMode(mode & 07777)
327+
printedOptsMap["mode"] = fmt.Sprintf("mode=%s", value)
317328

318-
printedOpts = append(printedOpts, fmt.Sprintf("mode=%s", modeStr))
319-
}
320-
rootKUID := creds.EffectiveKUID
321-
uidStr, ok := mopts["uid"]
322-
if ok {
323-
delete(mopts, "uid")
324-
uid, err := strconv.ParseUint(uidStr, 10, 32)
325-
if err != nil {
326-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid uid: %q", uidStr)
327-
return nil, nil, linuxerr.EINVAL
328-
}
329-
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
330-
if !kuid.Ok() {
331-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
332-
return nil, nil, linuxerr.EINVAL
333-
}
334-
rootKUID = kuid
329+
case "uid":
330+
uid, err := strconv.ParseUint(value, 10, 32)
331+
if err != nil {
332+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid uid: %q", value)
333+
return nil, nil, linuxerr.EINVAL
334+
}
335+
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
336+
if !kuid.Ok() {
337+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
338+
return nil, nil, linuxerr.EINVAL
339+
}
340+
rootKUID = kuid
341+
printedOptsMap["uid"] = fmt.Sprintf("uid=%s", value)
335342

336-
printedOpts = append(printedOpts, fmt.Sprintf("uid=%s", uidStr))
337-
}
338-
rootKGID := creds.EffectiveKGID
339-
gidStr, ok := mopts["gid"]
340-
if ok {
341-
delete(mopts, "gid")
342-
gid, err := strconv.ParseUint(gidStr, 10, 32)
343-
if err != nil {
344-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid gid: %q", gidStr)
345-
return nil, nil, linuxerr.EINVAL
346-
}
347-
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
348-
if !kgid.Ok() {
349-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
343+
case "gid":
344+
gid, err := strconv.ParseUint(value, 10, 32)
345+
if err != nil {
346+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid gid: %q", value)
347+
return nil, nil, linuxerr.EINVAL
348+
}
349+
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
350+
if !kgid.Ok() {
351+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
352+
return nil, nil, linuxerr.EINVAL
353+
}
354+
rootKGID = kgid
355+
printedOptsMap["gid"] = fmt.Sprintf("gid=%s", value)
356+
357+
case "noswap":
358+
// Accept, but ignore, noswap.
359+
360+
default:
361+
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unknown option: %s", key)
350362
return nil, nil, linuxerr.EINVAL
351363
}
352-
rootKGID = kgid
353-
354-
printedOpts = append(printedOpts, fmt.Sprintf("gid=%s", gidStr))
355364
}
356-
// Accept, but ignore, noswap
357-
delete(mopts, "noswap")
358365

359-
if len(mopts) != 0 {
360-
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unknown options: %v", mopts)
361-
return nil, nil, linuxerr.EINVAL
366+
var printedOpts []string
367+
if val, ok := printedOptsMap["size"]; ok {
368+
printedOpts = append(printedOpts, val)
369+
}
370+
if val, ok := printedOptsMap["nr_inodes"]; ok {
371+
printedOpts = append(printedOpts, val)
372+
}
373+
if val, ok := printedOptsMap["mode"]; ok {
374+
printedOpts = append(printedOpts, val)
375+
}
376+
if val, ok := printedOptsMap["uid"]; ok {
377+
printedOpts = append(printedOpts, val)
378+
}
379+
if val, ok := printedOptsMap["gid"]; ok {
380+
printedOpts = append(printedOpts, val)
362381
}
363382

364383
devMinor, err := vfsObj.GetAnonBlockDevMinor()

0 commit comments

Comments
 (0)