mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user