snapshotter: add "--remap-labels" support to overlayfs
Previously the only fuse-overlayfs supports "--remap-labels" option. Since idmapped mounts were landed to Linux kernel v5.12 it becomes possible to use it with overlayfs via mount_setattr() system call. The changes are based on experimental patchset published by Mauricio Vásquez #4734. Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io> Signed-off-by: Artem Kuzin <artem.kuzin@huawei.com> Signed-off-by: Ilya Hanov <ilya.hanov@huawei-partners.com>
This commit is contained in:
parent
e8ddf669f5
commit
723c88ce30
@ -268,16 +268,22 @@ func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snap
|
|||||||
// This can be used to recover mounts after calling View or Prepare.
|
// This can be used to recover mounts after calling View or Prepare.
|
||||||
func (o *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
|
func (o *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
|
||||||
var s storage.Snapshot
|
var s storage.Snapshot
|
||||||
|
var info snapshots.Info
|
||||||
if err := o.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
if err := o.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
||||||
s, err = storage.GetSnapshot(ctx, key)
|
s, err = storage.GetSnapshot(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get active mount: %w", err)
|
return fmt.Errorf("failed to get active mount: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, info, _, err = storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get snapshot info: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return o.mounts(s), nil
|
return o.mounts(s, info), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
||||||
@ -411,10 +417,46 @@ func (o *snapshotter) getCleanupDirectories(ctx context.Context) ([]string, erro
|
|||||||
return cleanup, nil
|
return cleanup, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateIDMapping(mapping string) error {
|
||||||
|
var (
|
||||||
|
hostID int
|
||||||
|
ctrID int
|
||||||
|
length int
|
||||||
|
)
|
||||||
|
|
||||||
|
if _, err := fmt.Sscanf(mapping, "%d:%d:%d", &ctrID, &hostID, &length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Almost impossible, but snapshots.WithLabels doesn't check it
|
||||||
|
if ctrID < 0 || hostID < 0 || length < 0 {
|
||||||
|
return fmt.Errorf("invalid mapping \"%d:%d:%d\"", ctrID, hostID, length)
|
||||||
|
}
|
||||||
|
if ctrID != 0 {
|
||||||
|
return fmt.Errorf("container mapping of 0 is only supported")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostID(mapping string) (int, error) {
|
||||||
|
var (
|
||||||
|
hostID int
|
||||||
|
ctrID int
|
||||||
|
length int
|
||||||
|
)
|
||||||
|
if err := validateIDMapping(mapping); err != nil {
|
||||||
|
return -1, fmt.Errorf("invalid mapping: %w", err)
|
||||||
|
}
|
||||||
|
if _, err := fmt.Sscanf(mapping, "%d:%d:%d", &ctrID, &hostID, &length); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return hostID, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {
|
func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {
|
||||||
var (
|
var (
|
||||||
s storage.Snapshot
|
s storage.Snapshot
|
||||||
td, path string
|
td, path string
|
||||||
|
info snapshots.Info
|
||||||
)
|
)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -445,14 +487,46 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
|
|||||||
return fmt.Errorf("failed to create snapshot: %w", err)
|
return fmt.Errorf("failed to create snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.ParentIDs) > 0 {
|
_, info, _, err = storage.GetInfo(ctx, key)
|
||||||
st, err := os.Stat(o.upperPath(s.ParentIDs[0]))
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("failed to get snapshot info: %w", err)
|
||||||
return fmt.Errorf("failed to stat parent: %w", err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
stat := st.Sys().(*syscall.Stat_t)
|
mappedUID := -1
|
||||||
if err := os.Lchown(filepath.Join(td, "fs"), int(stat.Uid), int(stat.Gid)); err != nil {
|
mappedGID := -1
|
||||||
|
// NOTE: if idmapped mounts' supported by hosted kernel there may be
|
||||||
|
// no parents at all, so overlayfs will not work and snapshotter
|
||||||
|
// will use bind mount. To be able to create file objects inside the
|
||||||
|
// rootfs -- just chown this only bound directory according to provided
|
||||||
|
// {uid,gid}map. In case of one/multiple parents -- chown upperdir.
|
||||||
|
if v, ok := info.Labels[snapshots.LabelSnapshotUIDMapping]; ok {
|
||||||
|
if mappedUID, err = hostID(v); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse UID mapping: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := info.Labels[snapshots.LabelSnapshotGIDMapping]; ok {
|
||||||
|
if mappedGID, err = hostID(v); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse GID mapping: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mappedUID == -1 || mappedGID == -1 {
|
||||||
|
if len(s.ParentIDs) > 0 {
|
||||||
|
st, err := os.Stat(o.upperPath(s.ParentIDs[0]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to stat parent: %w", err)
|
||||||
|
}
|
||||||
|
stat, ok := st.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("incompatible types after stat call: *syscall.Stat_t expected")
|
||||||
|
}
|
||||||
|
mappedUID = int(stat.Uid)
|
||||||
|
mappedGID = int(stat.Gid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mappedUID != -1 && mappedGID != -1 {
|
||||||
|
if err := os.Lchown(filepath.Join(td, "fs"), mappedUID, mappedGID); err != nil {
|
||||||
return fmt.Errorf("failed to chown: %w", err)
|
return fmt.Errorf("failed to chown: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -467,8 +541,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return o.mounts(s, info), nil
|
||||||
return o.mounts(s), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string, kind snapshots.Kind) (string, error) {
|
func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string, kind snapshots.Kind) (string, error) {
|
||||||
@ -490,7 +563,18 @@ func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string,
|
|||||||
return td, nil
|
return td, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
func (o *snapshotter) mounts(s storage.Snapshot, info snapshots.Info) []mount.Mount {
|
||||||
|
var options []string
|
||||||
|
|
||||||
|
if o.remapIds {
|
||||||
|
if v, ok := info.Labels[snapshots.LabelSnapshotUIDMapping]; ok {
|
||||||
|
options = append(options, fmt.Sprintf("uidmap=%s", v))
|
||||||
|
}
|
||||||
|
if v, ok := info.Labels[snapshots.LabelSnapshotGIDMapping]; ok {
|
||||||
|
options = append(options, fmt.Sprintf("gidmap=%s", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(s.ParentIDs) == 0 {
|
if len(s.ParentIDs) == 0 {
|
||||||
// if we only have one layer/no parents then just return a bind mount as overlay
|
// if we only have one layer/no parents then just return a bind mount as overlay
|
||||||
// will not work
|
// will not work
|
||||||
@ -498,20 +582,18 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
|||||||
if s.Kind == snapshots.KindView {
|
if s.Kind == snapshots.KindView {
|
||||||
roFlag = "ro"
|
roFlag = "ro"
|
||||||
}
|
}
|
||||||
|
|
||||||
return []mount.Mount{
|
return []mount.Mount{
|
||||||
{
|
{
|
||||||
Source: o.upperPath(s.ID),
|
Source: o.upperPath(s.ID),
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Options: []string{
|
Options: append(options,
|
||||||
roFlag,
|
roFlag,
|
||||||
"rbind",
|
"rbind",
|
||||||
},
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options := o.options
|
|
||||||
if s.Kind == snapshots.KindActive {
|
if s.Kind == snapshots.KindActive {
|
||||||
options = append(options,
|
options = append(options,
|
||||||
fmt.Sprintf("workdir=%s", o.workPath(s.ID)),
|
fmt.Sprintf("workdir=%s", o.workPath(s.ID)),
|
||||||
@ -522,10 +604,10 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
|||||||
{
|
{
|
||||||
Source: o.upperPath(s.ParentIDs[0]),
|
Source: o.upperPath(s.ParentIDs[0]),
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Options: []string{
|
Options: append(options,
|
||||||
"ro",
|
"ro",
|
||||||
"rbind",
|
"rbind",
|
||||||
},
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -534,8 +616,9 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
|||||||
for i := range s.ParentIDs {
|
for i := range s.ParentIDs {
|
||||||
parentPaths[i] = o.upperPath(s.ParentIDs[i])
|
parentPaths[i] = o.upperPath(s.ParentIDs[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
options = append(options, fmt.Sprintf("lowerdir=%s", strings.Join(parentPaths, ":")))
|
options = append(options, fmt.Sprintf("lowerdir=%s", strings.Join(parentPaths, ":")))
|
||||||
|
options = append(options, o.options...)
|
||||||
|
|
||||||
return []mount.Mount{
|
return []mount.Mount{
|
||||||
{
|
{
|
||||||
Type: "overlay",
|
Type: "overlay",
|
||||||
@ -543,7 +626,6 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
|||||||
Options: options,
|
Options: options,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) upperPath(id string) string {
|
func (o *snapshotter) upperPath(id string) string {
|
||||||
|
Loading…
Reference in New Issue
Block a user