Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions storage/docs/containers-storage.conf.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ based file systems.
Use ComposeFS to mount the data layers image. ComposeFS support is experimental and not recommended for production use.
This is a "string bool": "false"|"true" (cannot be native TOML boolean)

**sync**="none|filesystem"
Filesystem synchronization mode for layer creation. (default: "none")

- `none`: No synchronization.
Layer operations complete without calling syncfs().

- `filesystem`: Sync before completion.
Flush all pending writes to the file system before marking the layer
as present. This helps prevent data corruption if the system
crashes or loses power during layer operations.

### STORAGE OPTIONS FOR VFS TABLE

Expand All @@ -228,6 +238,16 @@ The `storage.options.vfs` table supports the following options:
ignore_chown_errors can be set to allow a non privileged user running with a single UID within a user namespace to run containers. The user can pull and use any image even those with multiple uids. Note multiple UIDs will be squashed down to the default uid in the container. These images will have no separation between the users in the container.
This is a "string bool": "false"|"true" (cannot be native TOML boolean)

**sync**="none|filesystem"
Filesystem synchronization mode for layer creation. (default: "none")

- `none`: No synchronization.
Layer operations complete without calling syncfs().

- `filesystem`: Sync before completion.
Flush all pending writes to the file system before marking the layer
as present. This helps prevent data corruption if the system
crashes or loses power during layer operations.

### STORAGE OPTIONS FOR ZFS TABLE

Expand Down
6 changes: 6 additions & 0 deletions storage/drivers/btrfs/btrfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
}

// SyncMode returns the sync mode configured for the driver.
// Btrfs does not support sync mode configuration, always returns SyncModeNone.
func (d *Driver) SyncMode() graphdriver.SyncMode {
return graphdriver.SyncModeNone
}

func free(p *C.char) {
C.free(unsafe.Pointer(p))
}
Expand Down
36 changes: 36 additions & 0 deletions storage/drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,40 @@ const (
FsMagicUnsupported = FsMagic(0x00000000)
)

// SyncMode defines when filesystem synchronization occurs during layer creation.
type SyncMode int

const (
// SyncModeNone - no synchronization
SyncModeNone SyncMode = iota
// SyncModeFilesystem - use syncfs() before layer marked as present
SyncModeFilesystem
)

// String returns the string representation of the sync mode
func (m SyncMode) String() string {
switch m {
case SyncModeNone:
return "none"
case SyncModeFilesystem:
return "filesystem"
default:
return "unknown"
}
}

// ParseSyncMode converts a string to SyncMode
func ParseSyncMode(s string) (SyncMode, error) {
switch strings.ToLower(strings.TrimSpace(s)) {
case "", "none":
return SyncModeNone, nil
case "filesystem":
return SyncModeFilesystem, nil
default:
return SyncModeNone, fmt.Errorf("invalid sync mode: %q", s)
}
}

var (
// All registered drivers
drivers map[string]InitFunc
Expand Down Expand Up @@ -169,6 +203,8 @@ type ProtoDriver interface {
AdditionalImageStores() []string
// Dedup performs deduplication of the driver's storage.
Dedup(DedupArgs) (DedupResult, error)
// SyncMode returns the sync mode configured for the driver.
SyncMode() SyncMode
}

// DiffDriver is the interface to use to implement graph diffs
Expand Down
30 changes: 24 additions & 6 deletions storage/drivers/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ type overlayOptions struct {
ignoreChownErrors bool
forceMask *os.FileMode
useComposefs bool
syncMode graphdriver.SyncMode
}

// Driver contains information about the home directory and the list of active mounts that are created using this driver.
Expand Down Expand Up @@ -593,6 +594,22 @@ func parseOptions(options []string) (*overlayOptions, error) {
}
m := os.FileMode(mask)
o.forceMask = &m
case "sync":
logrus.Debugf("overlay: sync=%s", val)
mode, err := graphdriver.ParseSyncMode(val)
if err != nil {
return nil, fmt.Errorf("invalid sync mode for overlay driver: %w", err)
}
// SyncModeNone and SyncModeFilesystem do not need any special handling because
// the overlay storage is always on the same file system as the metadata, thus
// the Syncfs() in layers.go covers also any file written by the overlay driver.
switch mode {
case graphdriver.SyncModeNone, graphdriver.SyncModeFilesystem:
// Nothing to do.
default:
return nil, fmt.Errorf("invalid mode for overlay driver: %q", val)
}
o.syncMode = mode
default:
return nil, fmt.Errorf("overlay: unknown option %s", key)
}
Expand Down Expand Up @@ -865,6 +882,11 @@ func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
}

// SyncMode returns the sync mode configured for the driver.
func (d *Driver) SyncMode() graphdriver.SyncMode {
return d.options.syncMode
}

// pruneStagingDirectories cleans up any staging directory that was leaked.
// It returns whether any staging directory is still present.
func (d *Driver) pruneStagingDirectories() bool {
Expand Down Expand Up @@ -2017,12 +2039,8 @@ func (d *Driver) Put(id string) error {
// If fusermount|fusermount3 failed to unmount the FUSE file system, make sure all
// pending changes are propagated to the file system
if !unmounted {
fd, err := unix.Open(mountpoint, unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err == nil {
if err := unix.Syncfs(fd); err != nil {
logrus.Debugf("Error Syncfs(%s) - %v", mountpoint, err)
}
unix.Close(fd)
if err := system.Syncfs(mountpoint); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this correct to not use the syncmode setting here and always sync?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this was already done before, I've only reused the new function now.

If you kill a FUSE file system, all the data in memory will be lost, even if the system didn't crash. This is a fallback path taken when doing it correctly through fusermount failed.

The syncfs here prevents that when fusermount failed (or it is not present), we do not lose data on fuse-overlayfs when we unmount it.

logrus.Debugf("Error Syncfs(%s) - %v", mountpoint, err)
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions storage/drivers/vfs/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
if err != nil {
return nil, err
}
case "vfs.sync", ".sync":
logrus.Debugf("vfs: sync=%s", val)
var err error
d.syncMode, err = graphdriver.ParseSyncMode(val)
if err != nil {
return nil, fmt.Errorf("invalid sync mode for vfs driver: %w", err)
}
// SyncModeNone and SyncModeFilesystem do not need any special handling because
// the vfs storage is always on the same file system as the metadata, thus the
// Syncfs() in layers.go covers also any file written by the vfs driver.
switch d.syncMode {
case graphdriver.SyncModeNone, graphdriver.SyncModeFilesystem:
// Nothing to do.
default:
return nil, fmt.Errorf("invalid mode for vfs driver: %q", val)
}
default:
return nil, fmt.Errorf("vfs driver does not support %s options", key)
}
Expand All @@ -84,6 +100,7 @@ type Driver struct {
home string
additionalHomes []string
ignoreChownErrors bool
syncMode graphdriver.SyncMode
naiveDiff graphdriver.DiffDriver
updater graphdriver.LayerIDMapUpdater
imageStore string
Expand All @@ -108,6 +125,11 @@ func (d *Driver) Cleanup() error {
return nil
}

// SyncMode returns the sync mode configured for the driver.
func (d *Driver) SyncMode() graphdriver.SyncMode {
return d.syncMode
}

type fileGetNilCloser struct {
storage.FileGetter
}
Expand Down
6 changes: 6 additions & 0 deletions storage/drivers/zfs/zfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ func (d *Driver) Cleanup() error {
return nil
}

// SyncMode returns the sync mode configured for the driver.
// ZFS does not support sync mode configuration, always returns SyncModeNone.
func (d *Driver) SyncMode() graphdriver.SyncMode {
return graphdriver.SyncModeNone
}

// Status returns information about the ZFS filesystem. It returns a two dimensional array of information
// such as pool name, dataset name, disk usage, parent quota and compression used.
// Currently it return 'Zpool', 'Zpool Health', 'Parent Dataset', 'Space Used By Parent',
Expand Down
Loading
Loading