mirror of
https://github.com/containerd/containerd.git
synced 2026-08-10 17:14:49 +00:00
Commit ee8ae9d569 ("Update erofs snapshotter to use mount manager")
temporarily removed the file-backed mount feature to adapt to the new
mount manager infrastructure as a quick start.
After the mount manager was introduced, a specific mount type can be
handled with a mount handler plugin to provide a dedicated mount
process (e.g. setup loopback devices in advance or calling external
mount helpers).
This commit adds a default EROFS mount handler for the Linux hosts
to set up loop devices for mount sources and "device=" external file
blobs if necessary (i.e. when file-backed mounts are unavailable),
allowing common runtimes such as runC to work directly, e.g.
``` sh
mount -t erofs /var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/1/layer.erofs \
/run/containerd/io.containerd.mount-manager.v1.bolt/t/346/1
```
will be handled as
``` sh
mount -t erofs /dev/loop1 /run/containerd/io.containerd.mount-manager.v1.bolt/t/346/1
```
and
``` sh
mount -t erofs /var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/7/fsmeta.erofs \
-odevice=/var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/1/layer.erofs,\
device=/var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/2/layer.erofs,\
...
device=/var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/7/layer.erofs
/run/containerd/io.containerd.mount-manager.v1.bolt/t/335/1
```
will be handled as
``` sh
mount -t erofs /dev/loop1 -odevice=/dev/loop2,device=/dev/loop3,... \
/run/containerd/io.containerd.mount-manager.v1.bolt/t/335/1
```
if file-backed mounts are unavailable.
For other host platforms (e.g. Darwin hosts) or specific runtimes
that require EROFS raw mounts instead of parsed mounts, this plugin
can be explicitly masked off by users.
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package mount
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/containerd/errdefs"
|
|
"github.com/containerd/log"
|
|
)
|
|
|
|
func LoopbackHandler() Handler {
|
|
return loopbackHandler{}
|
|
}
|
|
|
|
type loopbackHandler struct {
|
|
}
|
|
|
|
func (loopbackHandler) Mount(ctx context.Context, m Mount, mp string, _ []ActiveMount) (ActiveMount, error) {
|
|
if m.Type != "loop" {
|
|
return ActiveMount{}, errdefs.ErrNotImplemented
|
|
}
|
|
params := LoopParams{
|
|
Autoclear: true,
|
|
}
|
|
// TODO: Handle readonly
|
|
// TODO: Handle direct io
|
|
|
|
t := time.Now()
|
|
loop, err := SetupLoop(m.Source, params)
|
|
if err != nil {
|
|
return ActiveMount{}, err
|
|
}
|
|
defer loop.Close()
|
|
|
|
if err := os.Symlink(loop.Name(), mp); err != nil {
|
|
return ActiveMount{}, err
|
|
}
|
|
|
|
if err := setLoopAutoclear(loop, false); err != nil {
|
|
return ActiveMount{}, err
|
|
}
|
|
|
|
return ActiveMount{
|
|
Mount: m,
|
|
MountedAt: &t,
|
|
MountPoint: mp,
|
|
}, nil
|
|
}
|
|
|
|
func (loopbackHandler) Unmount(ctx context.Context, path string) error {
|
|
loopdev, err := os.Readlink(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
loop, err := os.Open(loopdev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer loop.Close()
|
|
|
|
if err := setLoopAutoclear(loop, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.Remove(path); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
|
|
// if removal of the symlink has failed, its possible for the loop device to get cleaned
|
|
// up and re-used. Leave the loop device around to prevent re-use and let a retry of
|
|
// Unmount clear it.`
|
|
if err := setLoopAutoclear(loop, false); err != nil {
|
|
// Very unlikely but log to track in case there is a problem with
|
|
// the loop being cleared and re-used.
|
|
log.G(ctx).WithError(err).Errorf("Failed to unset auto clear flag on symlink removal failure, loopback %q may be cleaned up while still being tracked", loopdev)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|