Skip to content

Commit 368c1dc

Browse files
avagingvisor-bot
authored andcommitted
runsc: create file as destination for file mount
Linux does not allow mounting files on top of directories and vice versa. This can lead to unexpected behavior and issues, such as `dockerd` failing to start when it encounters a file mount that appears as a directory entry during `/dev` enumeration. After this change, new mount destinations (files or directories) are created with permissions (0644 for files, 0755 for directories) consistent with `runc` behavior. Previously, new directories were created with 0777. The test case that request a bind mount into /dev/fd has been removed. /dev/fd is a symlink to /proc/self/fd. PiperOrigin-RevId: 753398796
1 parent 8db5d40 commit 368c1dc

File tree

2 files changed

+99
-23
lines changed

2 files changed

+99
-23
lines changed

runsc/boot/loader_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,6 @@ func createMountTestcases() []*CreateMountTestcase {
403403
Destination: "/dev",
404404
Type: "tmpfs",
405405
},
406-
{
407-
// Mounted by runsc by default.
408-
Destination: "/dev/fd",
409-
Type: "tmpfs",
410-
},
411406
{
412407
// Mount with the same prefix.
413408
Destination: "/dev/fd-foo",

runsc/boot/vfs.go

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,19 @@ func (c *containerMounter) prepareMounts() ([]mountInfo, error) {
813813
return mounts, nil
814814
}
815815

816+
func (c *containerMounter) getPathMode(ctx context.Context, creds *auth.Credentials, path *vfs.PathOperation) (linux.FileMode, error) {
817+
stat, err := c.k.VFS().StatAt(ctx, creds, path, &vfs.StatOptions{
818+
Mask: linux.STATX_TYPE,
819+
})
820+
if err != nil {
821+
return 0, err
822+
}
823+
if stat.Mask&linux.STATX_TYPE == 0 {
824+
return 0, fmt.Errorf("failed to get file type")
825+
}
826+
return linux.FileMode(stat.Mode), nil
827+
}
828+
816829
func (c *containerMounter) mountSubmount(ctx context.Context, spec *specs.Spec, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *mountInfo) (*vfs.Mount, error) {
817830
fsName, opts, err := getMountNameAndOptions(spec, conf, submount, c.productName, c.containerName)
818831
if err != nil {
@@ -823,10 +836,6 @@ func (c *containerMounter) mountSubmount(ctx context.Context, spec *specs.Spec,
823836
return nil, nil
824837
}
825838

826-
if err := c.makeMountPoint(ctx, creds, mns, submount.mount.Destination); err != nil {
827-
return nil, fmt.Errorf("creating mount point %q: %w", submount.mount.Destination, err)
828-
}
829-
830839
if submount.goferMountConf.ShouldUseOverlayfs() {
831840
log.Infof("Adding overlay on top of mount %q", submount.mount.Destination)
832841
var cleanup func()
@@ -838,18 +847,41 @@ func (c *containerMounter) mountSubmount(ctx context.Context, spec *specs.Spec,
838847
fsName = overlay.Name
839848
}
840849

850+
mount := submount.mount
841851
root := mns.Root(ctx)
842852
defer root.DecRef(ctx)
843853
target := &vfs.PathOperation{
844854
Root: root,
845855
Start: root,
846-
Path: fspath.Parse(submount.mount.Destination),
856+
Path: fspath.Parse(mount.Destination),
857+
}
858+
mnt, err := c.k.VFS().MountDisconnected(ctx, creds, "", fsName, opts)
859+
if err != nil {
860+
return nil, err
861+
}
862+
defer mnt.DecRef(ctx)
863+
864+
vd := vfs.MakeVirtualDentry(mnt, mnt.Root())
865+
rootPath := &vfs.PathOperation{
866+
Root: vd,
867+
Start: vd,
847868
}
848-
mnt, err := c.k.VFS().MountAt(ctx, creds, "", target, fsName, opts)
869+
870+
rootMode, err := c.getPathMode(ctx, creds, rootPath)
849871
if err != nil {
850-
return nil, fmt.Errorf("failed to mount %q (type: %s): %w, opts: %v", submount.mount.Destination, submount.mount.Type, err, opts)
872+
return nil, fmt.Errorf("failed to stat %q: %w", mount.Source, err)
873+
}
874+
if err := c.makeMountPoint(ctx, creds, mns, mount.Destination, rootMode); err != nil {
875+
return nil, fmt.Errorf("creating mount point %q: %w", mount.Destination, err)
851876
}
852-
log.Infof("Mounted %q to %q type: %s, internal-options: %q", submount.mount.Source, submount.mount.Destination, submount.mount.Type, opts.GetFilesystemOptions.Data)
877+
878+
if err := c.k.VFS().ConnectMountAt(ctx, creds, mnt, target); err != nil {
879+
return nil, fmt.Errorf("failed to mount %q (type: %s): %w, opts: %v",
880+
mount.Destination, mount.Type, err, opts)
881+
}
882+
883+
log.Infof("Mounted %q to %q type: %s, internal-options: %q",
884+
mount.Source, mount.Destination, mount.Type, opts.GetFilesystemOptions.Data)
853885
return mnt, nil
854886
}
855887

@@ -1225,7 +1257,17 @@ func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config
12251257
Path: fspath.Parse(mntInfo.mount.Destination),
12261258
}
12271259

1228-
if err := c.makeMountPoint(ctx, creds, mns, mntInfo.mount.Destination); err != nil {
1260+
vd := vfs.MakeVirtualDentry(newMnt, newMnt.Root())
1261+
rootPath := &vfs.PathOperation{
1262+
Root: vd,
1263+
Start: vd,
1264+
}
1265+
rootMode, err := c.getPathMode(ctx, creds, rootPath)
1266+
if err != nil {
1267+
return nil, err
1268+
}
1269+
1270+
if err := c.makeMountPoint(ctx, creds, mns, mntInfo.mount.Destination, rootMode); err != nil {
12291271
return nil, fmt.Errorf("creating mount point %q: %w", mntInfo.mount.Destination, err)
12301272
}
12311273

@@ -1236,24 +1278,63 @@ func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config
12361278
return newMnt, nil
12371279
}
12381280

1239-
func (c *containerMounter) makeMountPoint(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, dest string) error {
1281+
func (c *containerMounter) makeMountPoint(
1282+
ctx context.Context,
1283+
creds *auth.Credentials,
1284+
mns *vfs.MountNamespace,
1285+
dest string,
1286+
rootMode linux.FileMode,
1287+
) error {
12401288
root := mns.Root(ctx)
12411289
defer root.DecRef(ctx)
1290+
12421291
target := &vfs.PathOperation{
12431292
Root: root,
12441293
Start: root,
12451294
Path: fspath.Parse(dest),
12461295
}
1247-
// First check if mount point exists. When overlay is enabled, gofer doesn't
1248-
// allow changes to the FS, making MakeSytheticMountpoint() ineffective
1249-
// because MkdirAt fails with EROFS even if file exists.
1250-
vd, err := c.k.VFS().GetDentryAt(ctx, creds, target, &vfs.GetDentryOptions{})
1251-
if err == nil {
1252-
// File exists, we're done.
1253-
vd.DecRef(ctx)
1296+
1297+
fs := c.k.VFS()
1298+
1299+
mode, err := c.getPathMode(ctx, creds, target)
1300+
// First check if mount point exists.
1301+
switch {
1302+
case err == nil:
1303+
if mode.IsDir() != rootMode.IsDir() {
1304+
if rootMode.IsDir() {
1305+
return fmt.Errorf("mountpoint %q isn't a directory", dest)
1306+
} else {
1307+
return fmt.Errorf("mountpoint %q isn't not a file", dest)
1308+
}
1309+
}
1310+
// Target already exists.
12541311
return nil
1312+
case linuxerr.Equals(linuxerr.ENOENT, err):
1313+
// Expected, we will create the mount point.
1314+
default:
1315+
return fmt.Errorf("stat failed for %q during mountpoint creation: %w", dest, err)
1316+
}
1317+
1318+
mkdirOpts := &vfs.MkdirOptions{Mode: 0755, ForSyntheticMountpoint: true}
1319+
1320+
// Make sure the parent directory of target exists.
1321+
if err := fs.MkdirAllAt(ctx, path.Dir(dest), root, creds, mkdirOpts, true /* mustBeDir */); err != nil {
1322+
return fmt.Errorf("failed to create parent directory of mountpoint %q: %w", dest, err)
12551323
}
1256-
return c.k.VFS().MakeSyntheticMountpoint(ctx, dest, root, creds)
1324+
1325+
if rootMode.IsDir() {
1326+
if err := fs.MkdirAt(ctx, creds, target, mkdirOpts); err != nil {
1327+
return fmt.Errorf("failed to create directory mountpoint %q: %w", dest, err)
1328+
}
1329+
} else {
1330+
mknodOpts := &vfs.MknodOptions{
1331+
Mode: linux.FileMode(linux.S_IFREG | 0644),
1332+
}
1333+
if err := fs.MknodAt(ctx, creds, target, mknodOpts); err != nil {
1334+
return fmt.Errorf("failed to create file mountpoint %q: %w", dest, err)
1335+
}
1336+
}
1337+
return nil
12571338
}
12581339

12591340
// configureRestore returns an updated context.Context including filesystem

0 commit comments

Comments
 (0)