Skip to content

Commit 0d0b2bd

Browse files
committed
Mount devmapper xfs file system with "nouuid" option.
Two xfs file systems with same UUID can not be mounted on the same system. However devmapper snapshots will have same UUID as original filesystem. This patch fixes the bug by mounting a xfs file system with "nouuid" option. Signed-off-by: Henry Wang <[email protected]>
1 parent b521429 commit 0d0b2bd

2 files changed

Lines changed: 85 additions & 26 deletions

File tree

snapshots/devmapper/snapshotter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,8 @@ func (s *Snapshotter) buildMounts(ctx context.Context, snap storage.Snapshot, fi
504504
if fileSystemType == "" {
505505
log.G(ctx).Error("File system type cannot be empty")
506506
return nil
507+
} else if fileSystemType == fsTypeXFS {
508+
options = append(options, "nouuid")
507509
}
508510
if snap.Kind != snapshots.KindActive {
509511
options = append(options, "ro")

snapshots/devmapper/snapshotter_test.go

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,38 +46,13 @@ func TestSnapshotterSuite(t *testing.T) {
4646
logrus.SetLevel(logrus.DebugLevel)
4747

4848
snapshotterFn := func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) {
49-
// Create loopback devices for each test case
50-
_, loopDataDevice := createLoopbackDevice(t, root)
51-
_, loopMetaDevice := createLoopbackDevice(t, root)
52-
5349
poolName := fmt.Sprintf("containerd-snapshotter-suite-pool-%d", time.Now().Nanosecond())
54-
err := dmsetup.CreatePool(poolName, loopDataDevice, loopMetaDevice, 64*1024/dmsetup.SectorSize)
55-
assert.NilError(t, err, "failed to create pool %q", poolName)
56-
5750
config := &Config{
5851
RootPath: root,
5952
PoolName: poolName,
6053
BaseImageSize: "16Mb",
6154
}
62-
63-
snap, err := NewSnapshotter(context.Background(), config)
64-
if err != nil {
65-
return nil, nil, err
66-
}
67-
68-
// Remove device mapper pool and detach loop devices after test completes
69-
removePool := func() error {
70-
result := multierror.Append(
71-
snap.pool.RemovePool(ctx),
72-
mount.DetachLoopDevice(loopDataDevice, loopMetaDevice))
73-
74-
return result.ErrorOrNil()
75-
}
76-
77-
// Pool cleanup should be called before closing metadata store (as we need to retrieve device names)
78-
snap.cleanupFn = append([]closeFunc{removePool}, snap.cleanupFn...)
79-
80-
return snap, snap.Close, nil
55+
return createSnapshotter(ctx, t, config)
8156
}
8257

8358
testsuite.SnapshotterSuite(t, "devmapper", snapshotterFn)
@@ -165,3 +140,85 @@ func TestMkfsXfsNonDefault(t *testing.T) {
165140
err := mkfs(ctx, "xfs", "noquota", "")
166141
assert.ErrorContains(t, err, `mkfs.xfs couldn't initialize ""`)
167142
}
143+
144+
func TestMultipleXfsMounts(t *testing.T) {
145+
testutil.RequiresRoot(t)
146+
147+
logrus.SetLevel(logrus.DebugLevel)
148+
149+
ctx := context.Background()
150+
ctx = namespaces.WithNamespace(ctx, "testsuite")
151+
tempDir, err := os.MkdirTemp("", "snapshot-suite-usage")
152+
assert.NilError(t, err)
153+
defer os.RemoveAll(tempDir)
154+
155+
poolName := fmt.Sprintf("containerd-snapshotter-suite-pool-%d", time.Now().Nanosecond())
156+
config := &Config{
157+
RootPath: tempDir,
158+
PoolName: poolName,
159+
BaseImageSize: "16Mb",
160+
FileSystemType: "xfs",
161+
}
162+
snapshotter, closer, err := createSnapshotter(ctx, t, config)
163+
assert.NilError(t, err)
164+
defer closer()
165+
166+
var (
167+
sizeBytes int64 = 1048576 // 1MB
168+
baseApplier = fstest.Apply(fstest.CreateRandomFile("/a", 12345679, sizeBytes, 0777))
169+
)
170+
171+
// Create base layer
172+
mounts, err := snapshotter.Prepare(ctx, "prepare-1", "")
173+
assert.NilError(t, err)
174+
175+
root1, _ := os.MkdirTemp(os.TempDir(), "containerd-mount")
176+
defer func() {
177+
mount.UnmountAll(root1, 0)
178+
os.Remove(root1)
179+
}()
180+
err = mount.All(mounts, root1)
181+
assert.NilError(t, err)
182+
baseApplier.Apply(root1)
183+
snapshotter.Commit(ctx, "layer-1", "prepare-1")
184+
185+
// Create one child layer
186+
mounts, err = snapshotter.Prepare(ctx, "prepare-2", "layer-1")
187+
assert.NilError(t, err)
188+
189+
root2, _ := os.MkdirTemp(os.TempDir(), "containerd-mount")
190+
defer func() {
191+
mount.UnmountAll(root2, 0)
192+
os.Remove(root2)
193+
}()
194+
err = mount.All(mounts, root2)
195+
assert.NilError(t, err)
196+
}
197+
198+
func createSnapshotter(ctx context.Context, t *testing.T, config *Config) (snapshots.Snapshotter, func() error, error) {
199+
// Create loopback devices for each test case
200+
_, loopDataDevice := createLoopbackDevice(t, config.RootPath)
201+
_, loopMetaDevice := createLoopbackDevice(t, config.RootPath)
202+
203+
err := dmsetup.CreatePool(config.PoolName, loopDataDevice, loopMetaDevice, 64*1024/dmsetup.SectorSize)
204+
assert.NilError(t, err, "failed to create pool %q", config.PoolName)
205+
206+
snap, err := NewSnapshotter(ctx, config)
207+
if err != nil {
208+
return nil, nil, err
209+
}
210+
211+
// Remove device mapper pool and detach loop devices after test completes
212+
removePool := func() error {
213+
result := multierror.Append(
214+
snap.pool.RemovePool(ctx),
215+
mount.DetachLoopDevice(loopDataDevice, loopMetaDevice))
216+
217+
return result.ErrorOrNil()
218+
}
219+
220+
// Pool cleanup should be called before closing metadata store (as we need to retrieve device names)
221+
snap.cleanupFn = append([]closeFunc{removePool}, snap.cleanupFn...)
222+
223+
return snap, snap.Close, nil
224+
}

0 commit comments

Comments
 (0)