|
1 | 1 | package storage |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "archive/tar" |
| 5 | + "io" |
4 | 6 | "os" |
5 | 7 | "path/filepath" |
6 | 8 | "testing" |
@@ -629,3 +631,100 @@ func TestStoreDelete(t *testing.T) { |
629 | 631 |
|
630 | 632 | store.Free() |
631 | 633 | } |
| 634 | + |
| 635 | +// TestCreateContainerShifting verifies that when the overlay driver supports |
| 636 | +// shifting (idmapped mounts), CreateContainer stores the UID/GID maps on the |
| 637 | +// container layer so that Diff() can reverse-map host UIDs back to container |
| 638 | +// UIDs. It also verifies that UpdateLayerIDMap is not called (no diff1 |
| 639 | +// directory is created). |
| 640 | +func TestCreateContainerShifting(t *testing.T) { |
| 641 | + reexec.Init() |
| 642 | + |
| 643 | + if os.Getuid() != 0 { |
| 644 | + t.Skip("test requires root") |
| 645 | + } |
| 646 | + |
| 647 | + uidMap := []idtools.IDMap{{ContainerID: 0, HostID: 1000000, Size: 65536}} |
| 648 | + gidMap := []idtools.IDMap{{ContainerID: 0, HostID: 1000000, Size: 65536}} |
| 649 | + |
| 650 | + store := newTestStore(t, StoreOptions{ |
| 651 | + GraphDriverName: "overlay", |
| 652 | + UIDMap: uidMap, |
| 653 | + GIDMap: gidMap, |
| 654 | + }) |
| 655 | + defer func() { |
| 656 | + _, _ = store.Shutdown(true) |
| 657 | + store.Free() |
| 658 | + }() |
| 659 | + |
| 660 | + driver, err := store.GraphDriver() |
| 661 | + require.NoError(t, err) |
| 662 | + if !driver.SupportsShifting(uidMap, gidMap) { |
| 663 | + t.Skip("overlay driver does not support shifting on this kernel") |
| 664 | + } |
| 665 | + |
| 666 | + // Create a base layer with a file owned by container UID 0. |
| 667 | + baseLayer, err := store.CreateLayer("", "", nil, "", false, nil) |
| 668 | + require.NoError(t, err) |
| 669 | + |
| 670 | + image, err := store.CreateImage("", nil, baseLayer.ID, "", nil) |
| 671 | + require.NoError(t, err) |
| 672 | + |
| 673 | + // Create a container from the image with UID mappings. |
| 674 | + // Pass the maps via ContainerOptions, as buildah does. |
| 675 | + container, err := store.CreateContainer("", nil, image.ID, "", "", &ContainerOptions{ |
| 676 | + IDMappingOptions: IDMappingOptions{ |
| 677 | + UIDMap: uidMap, |
| 678 | + GIDMap: gidMap, |
| 679 | + }, |
| 680 | + }) |
| 681 | + require.NoError(t, err) |
| 682 | + |
| 683 | + // Verify the container layer has the UID/GID maps stored. |
| 684 | + containerLayer, err := store.Layer(container.LayerID) |
| 685 | + require.NoError(t, err) |
| 686 | + assert.Equal(t, uidMap, containerLayer.UIDMap, "container layer should have UID maps stored") |
| 687 | + assert.Equal(t, gidMap, containerLayer.GIDMap, "container layer should have GID maps stored") |
| 688 | + |
| 689 | + // Verify that UpdateLayerIDMap was NOT called: the overlay driver creates |
| 690 | + // a "diff1" directory when rotating diff dirs during UpdateLayerIDMap. |
| 691 | + graphRoot := store.GraphRoot() |
| 692 | + diff1Path := filepath.Join(graphRoot, "overlay", containerLayer.ID, "diff1") |
| 693 | + _, err = os.Stat(diff1Path) |
| 694 | + assert.True(t, os.IsNotExist(err), "diff1 should not exist (UpdateLayerIDMap should not have been called)") |
| 695 | + |
| 696 | + // Mount the container, write a file with host UID, unmount. |
| 697 | + mountPoint, err := store.Mount(container.ID, "") |
| 698 | + require.NoError(t, err) |
| 699 | + |
| 700 | + testFile := filepath.Join(mountPoint, "testfile") |
| 701 | + require.NoError(t, os.WriteFile(testFile, []byte("hello"), 0o644)) |
| 702 | + // Simulate what happens in a user namespace: the file gets the host UID. |
| 703 | + require.NoError(t, os.Chown(testFile, 1000000, 1000000)) |
| 704 | + |
| 705 | + _, err = store.Unmount(container.ID, false) |
| 706 | + require.NoError(t, err) |
| 707 | + |
| 708 | + // Generate a diff and verify that Diff() maps host UIDs back to |
| 709 | + // container UIDs using the stored maps (ToContainer translation). |
| 710 | + rc, err := store.Diff("", containerLayer.ID, nil) |
| 711 | + require.NoError(t, err) |
| 712 | + defer rc.Close() |
| 713 | + |
| 714 | + tr := tar.NewReader(rc) |
| 715 | + found := false |
| 716 | + for { |
| 717 | + hdr, err := tr.Next() |
| 718 | + if err == io.EOF { |
| 719 | + break |
| 720 | + } |
| 721 | + require.NoError(t, err) |
| 722 | + if hdr.Name == "testfile" { |
| 723 | + found = true |
| 724 | + assert.Equal(t, 0, hdr.Uid, "Diff() should translate host UID back to container UID 0") |
| 725 | + assert.Equal(t, 0, hdr.Gid, "Diff() should translate host GID back to container GID 0") |
| 726 | + break |
| 727 | + } |
| 728 | + } |
| 729 | + assert.True(t, found, "testfile should be present in the diff") |
| 730 | +} |
0 commit comments