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.
|
||||
func (o *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
|
||||
var s storage.Snapshot
|
||||
var info snapshots.Info
|
||||
if err := o.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
||||
s, err = storage.GetSnapshot(ctx, key)
|
||||
if err != nil {
|
||||
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
|
||||
}); err != nil {
|
||||
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 {
|
||||
@ -411,10 +417,46 @@ func (o *snapshotter) getCleanupDirectories(ctx context.Context) ([]string, erro
|
||||
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) {
|
||||
var (
|
||||
s storage.Snapshot
|
||||
td, path string
|
||||
info snapshots.Info
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
_, info, _, err = storage.GetInfo(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get snapshot info: %w", err)
|
||||
}
|
||||
|
||||
mappedUID := -1
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
stat := st.Sys().(*syscall.Stat_t)
|
||||
if err := os.Lchown(filepath.Join(td, "fs"), int(stat.Uid), int(stat.Gid)); err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -467,8 +541,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return o.mounts(s), nil
|
||||
return o.mounts(s, info), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 we only have one layer/no parents then just return a bind mount as overlay
|
||||
// will not work
|
||||
@ -498,20 +582,18 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
||||
if s.Kind == snapshots.KindView {
|
||||
roFlag = "ro"
|
||||
}
|
||||
|
||||
return []mount.Mount{
|
||||
{
|
||||
Source: o.upperPath(s.ID),
|
||||
Type: "bind",
|
||||
Options: []string{
|
||||
Options: append(options,
|
||||
roFlag,
|
||||
"rbind",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
options := o.options
|
||||
if s.Kind == snapshots.KindActive {
|
||||
options = append(options,
|
||||
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]),
|
||||
Type: "bind",
|
||||
Options: []string{
|
||||
Options: append(options,
|
||||
"ro",
|
||||
"rbind",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -534,8 +616,9 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
||||
for i := range s.ParentIDs {
|
||||
parentPaths[i] = o.upperPath(s.ParentIDs[i])
|
||||
}
|
||||
|
||||
options = append(options, fmt.Sprintf("lowerdir=%s", strings.Join(parentPaths, ":")))
|
||||
options = append(options, o.options...)
|
||||
|
||||
return []mount.Mount{
|
||||
{
|
||||
Type: "overlay",
|
||||
@ -543,7 +626,6 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
||||
Options: options,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (o *snapshotter) upperPath(id string) string {
|
||||
|
Loading…
Reference in New Issue
Block a user