Add ENAMETOOLONG checks in overlayfs.

Linux overlayfs uses the max of all layers' filename length limit. Do the same
in gVisor.

This is needed to get PHP runtime test ext/standard/tests/strings/007.phpt to
pass with overlayfs.

PiperOrigin-RevId: 493942515
This commit is contained in:
Ayush Ranjan
2022-12-08 11:04:37 -08:00
committed by gVisor bot
parent f14b4bb3ca
commit d17af25336
4 changed files with 60 additions and 0 deletions
+1
View File
@@ -270,6 +270,7 @@ docker-tests: load-basic $(RUNTIME_BIN)
@$(call install_runtime,$(RUNTIME)-fdlimit,--fdlimit=2000) # Used by TestRlimitNoFile.
@$(call install_runtime,$(RUNTIME)-dcache,--fdlimit=2000 --dcache=100) # Used by TestDentryCacheLimit.
@$(call install_runtime,$(RUNTIME)-host-uds,--host-uds=all) # Used by TestHostSocketConnect.
@$(call install_runtime,$(RUNTIME)-overlay,--overlay2=root:/tmp) # Used by TestOverlay*.
@$(call test_runtime,$(RUNTIME),$(INTEGRATION_TARGETS) //test/e2e:integration_runtime_test)
.PHONY: docker-tests
+9
View File
@@ -161,6 +161,9 @@ afterSymlink:
rp.Advance()
return d.parent, d.parent.topLookupLayer(), nil
}
if uint64(len(name)) > fs.maxFilenameLen {
return nil, lookupLayerNone, linuxerr.ENAMETOOLONG
}
child, topLookupLayer, err := fs.getChildLocked(ctx, d, name, ds)
if err != nil {
return nil, topLookupLayer, err
@@ -504,6 +507,9 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, ct
if name == "." || name == ".." {
return linuxerr.EEXIST
}
if uint64(len(name)) > fs.maxFilenameLen {
return linuxerr.ENAMETOOLONG
}
if parent.vfsd.IsDead() {
return linuxerr.ENOENT
}
@@ -1090,6 +1096,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
return linuxerr.EBUSY
}
if uint64(len(newName)) > fs.maxFilenameLen {
return linuxerr.ENAMETOOLONG
}
// Do not check for newName length, since different filesystem
// implementations impose different name limits. upperfs.RenameAt() will fail
// appropriately if it has to.
+32
View File
@@ -125,6 +125,9 @@ type filesystem struct {
// lastDirIno is the last inode number assigned to a directory. lastDirIno
// is protected by dirInoCacheMu.
lastDirIno uint64
// MaxFilenameLen is the maximum filename length allowed by the overlayfs.
maxFilenameLen uint64
}
// +stateify savable
@@ -264,9 +267,23 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
dirDevMinor: dirDevMinor,
lowerDevMinors: make(map[layerDevNumber]uint32),
dirInoCache: make(map[layerDevNoAndIno]uint64),
maxFilenameLen: linux.NAME_MAX,
}
fs.vfsfs.Init(vfsObj, &fstype, fs)
// Configure max filename length. Similar to what Linux does in
// fs/overlayfs/super.c:ovl_fill_super() -> ... -> ovl_check_namelen().
if fsopts.UpperRoot.Ok() {
if err := fs.updateMaxNameLen(ctx, creds, vfsObj, fs.opts.UpperRoot); err != nil {
ctx.Debugf("overlay.FilesystemType.GetFilesystem: failed to StatFSAt on upper layer root: %v", err)
}
}
for _, lowerRoot := range fsopts.LowerRoots {
if err := fs.updateMaxNameLen(ctx, creds, vfsObj, lowerRoot); err != nil {
ctx.Debugf("overlay.FilesystemType.GetFilesystem: failed to StatFSAt on lower layer root: %v", err)
}
}
// Construct the root dentry.
root := fs.newDentry()
root.refs = atomicbitops.FromInt64(1)
@@ -368,6 +385,21 @@ func (fs *filesystem) Release(ctx context.Context) {
}
}
// updateMaxNameLen is analogous to fs/overlayfs/super.c:ovl_check_namelen().
func (fs *filesystem) updateMaxNameLen(ctx context.Context, creds *auth.Credentials, vfsObj *vfs.VirtualFilesystem, vd vfs.VirtualDentry) error {
statfs, err := vfsObj.StatFSAt(ctx, creds, &vfs.PathOperation{
Root: vd,
Start: vd,
})
if err != nil {
return err
}
if statfs.NameLength > fs.maxFilenameLen {
fs.maxFilenameLen = statfs.NameLength
}
return nil
}
func (fs *filesystem) statFS(ctx context.Context) (linux.Statfs, error) {
// Always statfs the root of the topmost layer. Compare Linux's
// fs/overlayfs/super.c:ovl_statfs().
+18
View File
@@ -24,6 +24,7 @@ package integration
import (
"context"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
@@ -178,3 +179,20 @@ func TestHostSocketConnect(t *testing.T) {
}
wg.Wait()
}
func TestOverlayNameTooLong(t *testing.T) {
ctx := context.Background()
d := dockerutil.MakeContainerWithRuntime(ctx, t, "-overlay")
defer d.CleanUp(ctx)
opts := dockerutil.RunOpts{
Image: "basic/integrationtest",
WorkDir: "/root",
}
longName := strings.Repeat("a", unix.NAME_MAX+1)
if got, err := d.Run(ctx, opts, "bash", "-c", fmt.Sprintf("stat %s || true", longName)); err != nil {
t.Fatalf("docker run failed: %v", err)
} else if want := "File name too long"; !strings.Contains(got, want) {
t.Errorf("container output %q does not contain %q", got, want)
}
}