Skip to content

Commit 59d8e4b

Browse files
committed
storage, overlay: stop using symlinks for new layers
The overlay driver maintained a l/ directory of symlinks solely to shorten mount option strings below the kernel's page-size limit. This is unnecessary since mountOverlayFrom() already handles long mount options via fd-based mounting. New layers now store relative diff paths (e.g. "layer-id/diff") directly in the lower file instead of short symlink references. Old-format layers with l/ symlinks continue to work. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
1 parent a3a094f commit 59d8e4b

3 files changed

Lines changed: 25 additions & 327 deletions

File tree

storage/drivers/overlay/overlay.go

Lines changed: 25 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,6 @@ const (
9090

9191
tocArtifact = "toc"
9292
fsVerityDigestsArtifact = "fs-verity-digests"
93-
94-
// idLength represents the number of random characters
95-
// which can be used to create the unique link identifier
96-
// for every layer. If this value is too long then the
97-
// page size limit for the mount command may be exceeded.
98-
// The idLength should be selected such that following equation
99-
// is true (512 is a buffer for label metadata, 128 is the
100-
// number of lowers we want to be able to use without having
101-
// to use bind mounts to get all the way to the kernel limit).
102-
// ((idLength + len(linkDir) + 1) * 128) <= (pageSize - 512)
103-
idLength = 26
10493
)
10594

10695
type overlayOptions struct {
@@ -327,16 +316,10 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
327316
runhome := filepath.Join(options.RunRoot, filepath.Base(home))
328317

329318
// Create the driver home dir
330-
if err := os.MkdirAll(path.Join(home, linkDir), 0o755); err != nil {
319+
if err := os.MkdirAll(home, 0o755); err != nil {
331320
return nil, err
332321
}
333322

334-
if options.ImageStore != "" {
335-
if err := idtools.MkdirAllAs(path.Join(options.ImageStore, linkDir), 0o755, 0, 0); err != nil {
336-
return nil, err
337-
}
338-
}
339-
340323
if err := os.MkdirAll(runhome, 0o700); err != nil {
341324
return nil, err
342325
}
@@ -1038,7 +1021,7 @@ func (d *Driver) getLayerPermissions(parent string, uidMaps, gidMaps []idtools.I
10381021
}
10391022

10401023
func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnly bool) (retErr error) {
1041-
dir, homedir, _ := d.dir2(id, readOnly)
1024+
dir, _, _ := d.dir2(id, readOnly)
10421025

10431026
disableQuota := readOnly
10441027

@@ -1050,11 +1033,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
10501033
gidMaps = opts.IDMappings.GIDs()
10511034
}
10521035

1053-
// Make the link directory if it does not exist
1054-
if err := idtools.MkdirAllAs(path.Join(homedir, linkDir), 0o755, 0, 0); err != nil {
1055-
return err
1056-
}
1057-
10581036
idPair, forcedSt, st, err := d.getLayerPermissions(parent, uidMaps, gidMaps)
10591037
if err != nil {
10601038
return err
@@ -1066,8 +1044,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
10661044

10671045
if err := fileutils.Lexists(dir); err == nil {
10681046
logrus.Warnf("Trying to create a layer %#v while directory %q already exists; removing it first", id, dir)
1069-
// Don’t just os.RemoveAll(dir) here; d.Remove also removes the link in linkDir,
1070-
// so that we can’t end up with two symlinks in linkDir pointing to the same layer.
1047+
// Don't just os.RemoveAll(dir) here; d.Remove also handles proper cleanup.
10711048
if err := d.Remove(id); err != nil {
10721049
return fmt.Errorf("removing a pre-existing layer directory %q: %w", dir, err)
10731050
}
@@ -1119,18 +1096,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
11191096
}
11201097
}
11211098

1122-
lid := generateID(idLength)
1123-
1124-
linkBase := path.Join("..", id, "diff")
1125-
if err := os.Symlink(linkBase, path.Join(homedir, linkDir, lid)); err != nil {
1126-
return err
1127-
}
1128-
1129-
// Write link id to link file
1130-
if err := os.WriteFile(path.Join(dir, "link"), []byte(lid), 0o644); err != nil {
1131-
return err
1132-
}
1133-
11341099
if err := idtools.MkdirAs(path.Join(dir, "work"), 0o700, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil {
11351100
return err
11361101
}
@@ -1190,22 +1155,7 @@ func (d *Driver) getLower(parent string) (string, error) {
11901155
return "", err
11911156
}
11921157

1193-
// Read Parent link fileA
1194-
parentLink, err := os.ReadFile(path.Join(parentDir, "link"))
1195-
if err != nil {
1196-
if !os.IsNotExist(err) {
1197-
return "", err
1198-
}
1199-
logrus.Warnf("Can't read parent link %q because it does not exist. Going through storage to recreate the missing links.", path.Join(parentDir, "link"))
1200-
if err := d.recreateSymlinks(); err != nil {
1201-
return "", fmt.Errorf("recreating the links: %w", err)
1202-
}
1203-
parentLink, err = os.ReadFile(path.Join(parentDir, "link"))
1204-
if err != nil {
1205-
return "", err
1206-
}
1207-
}
1208-
lowers := []string{path.Join(linkDir, string(parentLink))}
1158+
lowers := []string{path.Join(parent, "diff")}
12091159

12101160
parentLower, err := os.ReadFile(path.Join(parentDir, lowerFile))
12111161
if err == nil {
@@ -1266,24 +1216,15 @@ func (d *Driver) getLowerDirs(id string) ([]string, error) {
12661216
for s := range strings.SplitSeq(string(lowers), ":") {
12671217
lower := d.dir(s)
12681218
lp, err := os.Readlink(lower)
1269-
// if the link does not exist, we lost the symlinks during a sudden reboot.
1270-
// Let's go ahead and recreate those symlinks.
12711219
if err != nil {
1272-
if os.IsNotExist(err) {
1273-
logrus.Warnf("Can't read link %q because it does not exist. A storage corruption might have occurred, attempting to recreate the missing symlinks. It might be best wipe the storage to avoid further errors due to storage corruption.", lower)
1274-
if err := d.recreateSymlinks(); err != nil {
1275-
return nil, fmt.Errorf("recreating the missing symlinks: %w", err)
1276-
}
1277-
// let's call Readlink on lower again now that we have recreated the missing symlinks
1278-
lp, err = os.Readlink(lower)
1279-
if err != nil {
1280-
return nil, err
1281-
}
1282-
} else {
1283-
return nil, err
1220+
if errors.Is(err, syscall.EINVAL) {
1221+
// Not a symlink: new format, lower is already the diff path.
1222+
lowersArray = append(lowersArray, lower)
1223+
continue
12841224
}
1225+
return nil, err
12851226
}
1286-
lowersArray = append(lowersArray, path.Clean(d.dir(path.Join("link", lp))))
1227+
lowersArray = append(lowersArray, d.dir(path.Join("link", lp)))
12871228
}
12881229
} else if !os.IsNotExist(err) {
12891230
return nil, err
@@ -1393,112 +1334,6 @@ func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
13931334
return t.Cleanup, nil
13941335
}
13951336

1396-
// recreateSymlinks goes through the driver's home directory and checks if the diff directory
1397-
// under each layer has a symlink created for it under the linkDir. If the symlink does not
1398-
// exist, it creates them
1399-
func (d *Driver) recreateSymlinks() error {
1400-
// We have at most 3 corrective actions per layer, so 10 iterations is plenty.
1401-
const maxIterations = 10
1402-
1403-
// List all the directories under the home directory
1404-
dirs, err := os.ReadDir(d.home)
1405-
if err != nil {
1406-
return fmt.Errorf("reading driver home directory %q: %w", d.home, err)
1407-
}
1408-
// This makes the link directory if it doesn't exist
1409-
if err := idtools.MkdirAllAs(path.Join(d.home, linkDir), 0o755, 0, 0); err != nil {
1410-
return err
1411-
}
1412-
// Keep looping as long as we take some corrective action in each iteration
1413-
var errs error
1414-
madeProgress := true
1415-
iterations := 0
1416-
for madeProgress {
1417-
errs = nil
1418-
madeProgress = false
1419-
// Check that for each layer, there's a link in "l" with the name in
1420-
// the layer's "link" file that points to the layer's "diff" directory.
1421-
for _, dir := range dirs {
1422-
// Skip over the linkDir, stagingDir, tempDirName and anything that is not a directory
1423-
if dir.Name() == linkDir || dir.Name() == stagingDir || dir.Name() == tempDirName || !dir.IsDir() {
1424-
continue
1425-
}
1426-
// Read the "link" file under each layer to get the name of the symlink
1427-
data, err := os.ReadFile(path.Join(d.dir(dir.Name()), "link"))
1428-
if err != nil {
1429-
errs = errors.Join(errs, fmt.Errorf("reading name of symlink for %q: %w", dir.Name(), err))
1430-
continue
1431-
}
1432-
linkPath := path.Join(d.home, linkDir, strings.Trim(string(data), "\n"))
1433-
// Check if the symlink exists, and if it doesn't, create it again with the
1434-
// name we got from the "link" file
1435-
err = fileutils.Lexists(linkPath)
1436-
if err != nil && os.IsNotExist(err) {
1437-
if err := os.Symlink(path.Join("..", dir.Name(), "diff"), linkPath); err != nil {
1438-
errs = errors.Join(errs, err)
1439-
continue
1440-
}
1441-
madeProgress = true
1442-
} else if err != nil {
1443-
errs = errors.Join(errs, err)
1444-
continue
1445-
}
1446-
}
1447-
1448-
// linkDirFullPath is the full path to the linkDir
1449-
linkDirFullPath := filepath.Join(d.home, "l")
1450-
// Now check if we somehow lost a "link" file, by making sure
1451-
// that each symlink we have corresponds to one.
1452-
links, err := os.ReadDir(linkDirFullPath)
1453-
if err != nil {
1454-
errs = errors.Join(errs, err)
1455-
continue
1456-
}
1457-
// Go through all of the symlinks in the "l" directory
1458-
for _, link := range links {
1459-
// Read the symlink's target, which should be "../$layer/diff"
1460-
target, err := os.Readlink(filepath.Join(linkDirFullPath, link.Name()))
1461-
if err != nil {
1462-
errs = errors.Join(errs, err)
1463-
continue
1464-
}
1465-
targetComponents := strings.Split(target, string(os.PathSeparator))
1466-
if len(targetComponents) != 3 || targetComponents[0] != ".." || targetComponents[2] != "diff" {
1467-
errs = errors.Join(errs, fmt.Errorf("link target of %q looks weird: %q", link, target))
1468-
// force the link to be recreated on the next pass
1469-
if err := os.Remove(filepath.Join(linkDirFullPath, link.Name())); err != nil {
1470-
if !os.IsNotExist(err) {
1471-
errs = errors.Join(errs, fmt.Errorf("removing link %q: %w", link, err))
1472-
} // else don’t report any error, but also don’t set madeProgress.
1473-
continue
1474-
}
1475-
madeProgress = true
1476-
continue
1477-
}
1478-
// Reconstruct the name of the target's link file and check that
1479-
// it has the basename of our symlink in it.
1480-
targetID := targetComponents[1]
1481-
linkFile := filepath.Join(d.dir(targetID), "link")
1482-
data, err := os.ReadFile(linkFile)
1483-
if err != nil || string(data) != link.Name() {
1484-
// NOTE: If two or more links point to the same target, we will update linkFile
1485-
// with every value of link.Name(), and set madeProgress = true every time.
1486-
if err := os.WriteFile(linkFile, []byte(link.Name()), 0o644); err != nil {
1487-
errs = errors.Join(errs, fmt.Errorf("correcting link for layer %s: %w", targetID, err))
1488-
continue
1489-
}
1490-
madeProgress = true
1491-
}
1492-
}
1493-
iterations++
1494-
if iterations >= maxIterations {
1495-
errs = errors.Join(errs, fmt.Errorf("reached %d iterations in overlay graph driver’s recreateSymlink, giving up", iterations))
1496-
break
1497-
}
1498-
}
1499-
return errs
1500-
}
1501-
15021337
// Get creates and mounts the required file system for the given id and returns the mount path.
15031338
func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
15041339
return d.get(id, false, options)
@@ -1692,45 +1527,25 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
16921527
continue
16931528
}
16941529

1695-
lower := ""
1696-
newpath := path.Join(d.home, l)
1697-
if st, err := os.Stat(newpath); err != nil {
1698-
for _, p := range d.getAllImageStores() {
1699-
lower = path.Join(p, d.name, l)
1700-
if st2, err2 := os.Stat(lower); err2 == nil {
1701-
if !permsKnown {
1702-
perms = st2.Mode()
1703-
permsKnown = true
1704-
}
1705-
break
1706-
}
1707-
lower = ""
1708-
}
1709-
// if it is a "not found" error, that means the symlinks were lost in a sudden reboot
1710-
// so call the recreateSymlinks function to go through all the layer dirs and recreate
1711-
// the symlinks with the name from their respective "link" files
1712-
if lower == "" && os.IsNotExist(err) {
1713-
logrus.Warnf("Can't stat lower layer %q because it does not exist. Going through storage to recreate the missing symlinks.", newpath)
1714-
if err := d.recreateSymlinks(); err != nil {
1715-
return "", fmt.Errorf("recreating the missing symlinks: %w", err)
1716-
}
1717-
lower = newpath
1718-
} else if lower == "" {
1719-
return "", fmt.Errorf("can't stat lower layer %q: %w", newpath, err)
1530+
lower := d.dir(l)
1531+
lp, err := os.Readlink(lower)
1532+
if err != nil {
1533+
if !errors.Is(err, syscall.EINVAL) {
1534+
return "", fmt.Errorf("can't resolve lower layer %q: %w", lower, err)
17201535
}
1536+
// Not a symlink: lower is already the diff path.
17211537
} else {
1722-
if !permsKnown {
1723-
perms = st.Mode()
1724-
permsKnown = true
1725-
}
1726-
lower = newpath
1538+
lower = d.dir(path.Join("link", lp))
17271539
}
1728-
1729-
linkContent, err := os.Readlink(lower)
1730-
if err != nil {
1731-
return "", err
1540+
if st, err := os.Stat(lower); err != nil {
1541+
return "", fmt.Errorf("can't stat lower layer %q: %w", lower, err)
1542+
} else if !permsKnown {
1543+
perms = st.Mode()
1544+
permsKnown = true
17321545
}
1733-
lowerID := filepath.Base(filepath.Dir(linkContent))
1546+
1547+
// lowerID is the layer directory name, used for composefs blob lookup.
1548+
lowerID := filepath.Base(filepath.Dir(lower))
17341549
composefsMount, err := maybeAddComposefsMount(lowerID, i+1, readWrite)
17351550
if err != nil {
17361551
return "", err

storage/drivers/overlay/randomid.go

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)