@@ -813,6 +813,20 @@ func (c *containerMounter) prepareMounts() ([]mountInfo, error) {
813813 return mounts , nil
814814}
815815
816+ // getPathMode returns file mode for the specified path.
817+ func (c * containerMounter ) getPathMode (ctx context.Context , creds * auth.Credentials , path * vfs.PathOperation ) (linux.FileMode , error ) {
818+ stat , err := c .k .VFS ().StatAt (ctx , creds , path , & vfs.StatOptions {
819+ Mask : linux .STATX_TYPE ,
820+ })
821+ if err != nil {
822+ return 0 , err
823+ }
824+ if stat .Mask & linux .STATX_TYPE == 0 {
825+ return 0 , fmt .Errorf ("failed to get file type" )
826+ }
827+ return linux .FileMode (stat .Mode ), nil
828+ }
829+
816830func (c * containerMounter ) mountSubmount (ctx context.Context , spec * specs.Spec , conf * config.Config , mns * vfs.MountNamespace , creds * auth.Credentials , submount * mountInfo ) (* vfs.Mount , error ) {
817831 fsName , opts , err := getMountNameAndOptions (spec , conf , submount , c .productName , c .containerName )
818832 if err != nil {
@@ -823,10 +837,6 @@ func (c *containerMounter) mountSubmount(ctx context.Context, spec *specs.Spec,
823837 return nil , nil
824838 }
825839
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-
830840 if submount .goferMountConf .ShouldUseOverlayfs () {
831841 log .Infof ("Adding overlay on top of mount %q" , submount .mount .Destination )
832842 var cleanup func ()
@@ -838,18 +848,42 @@ func (c *containerMounter) mountSubmount(ctx context.Context, spec *specs.Spec,
838848 fsName = overlay .Name
839849 }
840850
851+ mount := submount .mount
841852 root := mns .Root (ctx )
842853 defer root .DecRef (ctx )
843854 target := & vfs.PathOperation {
844855 Root : root ,
845856 Start : root ,
846- Path : fspath .Parse (submount .mount .Destination ),
857+ Path : fspath .Parse (mount .Destination ),
858+ }
859+ mnt , err := c .k .VFS ().MountDisconnected (ctx , creds , "" , fsName , opts )
860+ if err != nil {
861+ return nil , fmt .Errorf ("mounting %q (type:%s, dest: %q): %w, opts: %v" ,
862+ mount .Source , mount .Type , mount .Destination , err , opts )
847863 }
848- mnt , err := c .k .VFS ().MountAt (ctx , creds , "" , target , fsName , opts )
864+ defer mnt .DecRef (ctx )
865+
866+ vd := vfs .MakeVirtualDentry (mnt , mnt .Root ())
867+ rootPath := & vfs.PathOperation {
868+ Root : vd ,
869+ Start : vd ,
870+ }
871+
872+ rootMode , err := c .getPathMode (ctx , creds , rootPath )
849873 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 )
874+ return nil , fmt .Errorf ("stat mount %q (dest: %q): %w" , mount .Source , mount .Destination , err )
875+ }
876+ if err := c .makeMountPoint (ctx , creds , mns , mount .Destination , rootMode ); err != nil {
877+ return nil , fmt .Errorf ("creating mount point %q: %w" , mount .Destination , err )
851878 }
852- log .Infof ("Mounted %q to %q type: %s, internal-options: %q" , submount .mount .Source , submount .mount .Destination , submount .mount .Type , opts .GetFilesystemOptions .Data )
879+
880+ if err := c .k .VFS ().ConnectMountAt (ctx , creds , mnt , target ); err != nil {
881+ return nil , fmt .Errorf ("attaching %q to %q (type: %s): %w, opts: %v" ,
882+ mount .Source , mount .Destination , mount .Type , err , opts )
883+ }
884+
885+ log .Infof ("Mounted %q to %q type: %s, internal-options: %q" ,
886+ mount .Source , mount .Destination , mount .Type , opts .GetFilesystemOptions .Data )
853887 return mnt , nil
854888}
855889
@@ -1225,7 +1259,17 @@ func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config
12251259 Path : fspath .Parse (mntInfo .mount .Destination ),
12261260 }
12271261
1228- if err := c .makeMountPoint (ctx , creds , mns , mntInfo .mount .Destination ); err != nil {
1262+ vd := vfs .MakeVirtualDentry (newMnt , newMnt .Root ())
1263+ rootPath := & vfs.PathOperation {
1264+ Root : vd ,
1265+ Start : vd ,
1266+ }
1267+ rootMode , err := c .getPathMode (ctx , creds , rootPath )
1268+ if err != nil {
1269+ return nil , err
1270+ }
1271+
1272+ if err := c .makeMountPoint (ctx , creds , mns , mntInfo .mount .Destination , rootMode ); err != nil {
12291273 return nil , fmt .Errorf ("creating mount point %q: %w" , mntInfo .mount .Destination , err )
12301274 }
12311275
@@ -1236,24 +1280,67 @@ func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config
12361280 return newMnt , nil
12371281}
12381282
1239- func (c * containerMounter ) makeMountPoint (ctx context.Context , creds * auth.Credentials , mns * vfs.MountNamespace , dest string ) error {
1283+ // makeMountPoint creates the mount point destination, ensuring its type (file
1284+ // or directory) matches `rootMode`. If the destination already exists, it just
1285+ // verifies that its type is consistent with `rootMode`; otherwise, the ENOTDIR
1286+ // error is returned.
1287+ func (c * containerMounter ) makeMountPoint (
1288+ ctx context.Context ,
1289+ creds * auth.Credentials ,
1290+ mns * vfs.MountNamespace ,
1291+ dest string ,
1292+ rootMode linux.FileMode ,
1293+ ) error {
12401294 root := mns .Root (ctx )
12411295 defer root .DecRef (ctx )
1296+
12421297 target := & vfs.PathOperation {
12431298 Root : root ,
12441299 Start : root ,
12451300 Path : fspath .Parse (dest ),
12461301 }
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 )
1302+
1303+ fs := c .k .VFS ()
1304+
1305+ mode , err := c .getPathMode (ctx , creds , target )
1306+ // First check if mount point exists.
1307+ switch {
1308+ case err == nil :
1309+ if mode .IsDir () != rootMode .IsDir () {
1310+ if rootMode .IsDir () {
1311+ return fmt .Errorf ("mountpoint %q isn't a directory" , dest )
1312+ } else {
1313+ return fmt .Errorf ("mountpoint %q isn't not a file" , dest )
1314+ }
1315+ }
1316+ // Target already exists.
12541317 return nil
1318+ case linuxerr .Equals (linuxerr .ENOENT , err ):
1319+ // Expected, we will create the mount point.
1320+ default :
1321+ return fmt .Errorf ("stat failed for %q during mountpoint creation: %w" , dest , err )
1322+ }
1323+
1324+ mkdirOpts := & vfs.MkdirOptions {Mode : 0755 , ForSyntheticMountpoint : true }
1325+
1326+ // Make sure the parent directory of target exists.
1327+ if err := fs .MkdirAllAt (ctx , path .Dir (dest ), root , creds , mkdirOpts , true /* mustBeDir */ ); err != nil {
1328+ return fmt .Errorf ("failed to create parent directory of mountpoint %q: %w" , dest , err )
1329+ }
1330+
1331+ if rootMode .IsDir () {
1332+ if err := fs .MkdirAt (ctx , creds , target , mkdirOpts ); err != nil {
1333+ return fmt .Errorf ("failed to create directory mountpoint %q: %w" , dest , err )
1334+ }
1335+ } else {
1336+ mknodOpts := & vfs.MknodOptions {
1337+ Mode : linux .FileMode (linux .S_IFREG | 0644 ),
1338+ }
1339+ if err := fs .MknodAt (ctx , creds , target , mknodOpts ); err != nil {
1340+ return fmt .Errorf ("failed to create file mountpoint %q: %w" , dest , err )
1341+ }
12551342 }
1256- return c . k . VFS (). MakeSyntheticMountpoint ( ctx , dest , root , creds )
1343+ return nil
12571344}
12581345
12591346// configureRestore returns an updated context.Context including filesystem
0 commit comments