diff --git a/pkg/snapshot/overlay.go b/pkg/snapshot/overlay.go index c74e42cd..1b224dd2 100644 --- a/pkg/snapshot/overlay.go +++ b/pkg/snapshot/overlay.go @@ -280,7 +280,6 @@ func NewSnapshotter(bootConfig *BootConfig, opts ...Opt) (snapshots.Snapshotter, // Stat returns the info for an active or committed snapshot by the key. func (o *snapshotter) Stat(ctx context.Context, key string) (_ snapshots.Info, retErr error) { - log.G(ctx).Infof("Stat (key: %s)", key) start := time.Now() defer func() { if retErr != nil { @@ -335,7 +334,6 @@ func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpath // Usage returns the resources taken by the snapshot identified by key. func (o *snapshotter) Usage(ctx context.Context, key string) (_ snapshots.Usage, retErr error) { - log.G(ctx).Infof("Usage (key: %s)", key) start := time.Now() defer func() { if retErr != nil { @@ -375,31 +373,40 @@ func (o *snapshotter) getWritableType(ctx context.Context, id string, info snaps defer func() { log.G(ctx).Infof("snapshot R/W label: %s", mode) }() - // check image type (OCIv1 or overlaybd) - if id != "" { - if _, err := o.loadBackingStoreConfig(id); err != nil { - log.G(ctx).Debugf("[%s] is not an overlaybd image.", id) + + parseMode := func(m string) string { + switch m { + case "dir": + return RwDir + case "dev": + return RwDev + default: return RoDir } - } else { - log.G(ctx).Debugf("empty snID get. It should be an initial layer.") } - // overlaybd - rwMode := func(m string) string { - if m == "dir" { - return RwDir - } - if m == "dev" { - return RwDev + + // check image type (OCIv1 or overlaybd) + if id == "" { + log.G(ctx).Debugf("empty snID get. It should be an initial layer.") + // An initial layer without an explicit R/W label is prepared for + // pulling an image rather than for a container rootfs, keep it + // read-only instead of falling back to the global rwMode. + if m, ok := info.Labels[label.SupportReadWriteMode]; ok { + return parseMode(m) } return RoDir } - m, ok := info.Labels[label.SupportReadWriteMode] - if !ok { - return rwMode(o.rwMode) + + if _, err := o.loadBackingStoreConfig(id); err != nil { + log.G(ctx).Debugf("[%s] is not an overlaybd image.", id) + return RoDir } - return rwMode(m) + // overlaybd + if m, ok := info.Labels[label.SupportReadWriteMode]; ok { + return parseMode(m) + } + return parseMode(o.rwMode) } func (o *snapshotter) checkTurboOCI(labels map[string]string) (bool, string, string) { @@ -1544,6 +1551,10 @@ func (o *snapshotter) turboOCIFsMeta(id string) (string, string) { log.L.Warn("erofs is not supported on this system, fallback to other fs type") continue } + if fsType == "erofs" && o.rwMode != RoDir { + log.L.Warn("erofs is read-only, it cannot be used as a writable rootfs, fallback to other fs type") + continue + } return fsmeta, fsType } else if !errors.Is(err, os.ErrNotExist) { log.L.Errorf("error while checking fs meta file: %s", err) diff --git a/pkg/snapshot/storage.go b/pkg/snapshot/storage.go index e93ef1a6..5fb35bc0 100644 --- a/pkg/snapshot/storage.go +++ b/pkg/snapshot/storage.go @@ -51,7 +51,9 @@ import ( ) const ( - maxAttachAttempts = 50 + // maxAttachAttempts is the number of 10ms-spaced polls waiting for a + // device node or a target to show up, i.e. a 5s timeout. + maxAttachAttempts = 500 // hba number used to create tcmu devices in configfs // all overlaybd devices are configured in /sys/kernel/config/target/core/user_999999999/ @@ -463,12 +465,25 @@ func AttachDevice(ctx context.Context, params *AttachDeviceParams) (devName stri time.Sleep(10 * time.Millisecond) break // retry } - devName = device - break + goto waitDevReady + } + } + return "", fmt.Errorf("timeout to find device for snID: %s, lastErr: %w", snID, e) + +waitDevReady: + // Wait for /dev/sdX block device node to be fully ready. + // After sysfs timeout is set, udev may still be creating the device node. + for _, wait := range []time.Duration{5, 10, 20, 50, 100, 100, 100, 100, 100, 100} { + f, err := os.Open(devName) + if err == nil { + f.Close() + log.G(ctx).Infof("Device has been created. {id: %s: dev: %s}", snID, devName) + return devName, nil } + e = fmt.Errorf("block device %s not ready yet: %w", devName, err) + time.Sleep(wait * time.Millisecond) } - log.G(ctx).Infof("Device has been created. {id: %s: dev: %s}", snID, devName) - return devName, nil + return "", fmt.Errorf("block device %s not ready after retries", devName) } // attachAndMountBlockDevice