Skip to content

Commit dff75cb

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 7ea6421 commit dff75cb

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
@@ -91,17 +91,6 @@ const (
9191

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

10796
type overlayOptions struct {
@@ -328,16 +317,10 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
328317
runhome := filepath.Join(options.RunRoot, filepath.Base(home))
329318

330319
// Create the driver home dir
331-
if err := os.MkdirAll(path.Join(home, linkDir), 0o755); err != nil {
320+
if err := os.MkdirAll(home, 0o755); err != nil {
332321
return nil, err
333322
}
334323

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

10411024
func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnly bool) (retErr error) {
1042-
dir, homedir, _ := d.dir2(id, readOnly)
1025+
dir, _, _ := d.dir2(id, readOnly)
10431026

10441027
disableQuota := readOnly
10451028

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

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

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

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

1194-
// Read Parent link fileA
1195-
parentLink, err := os.ReadFile(path.Join(parentDir, "link"))
1196-
if err != nil {
1197-
if !os.IsNotExist(err) {
1198-
return "", err
1199-
}
1200-
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"))
1201-
if err := d.recreateSymlinks(); err != nil {
1202-
return "", fmt.Errorf("recreating the links: %w", err)
1203-
}
1204-
parentLink, err = os.ReadFile(path.Join(parentDir, "link"))
1205-
if err != nil {
1206-
return "", err
1207-
}
1208-
}
1209-
lowers := []string{path.Join(linkDir, string(parentLink))}
1159+
lowers := []string{path.Join(parent, "diff")}
12101160

12111161
parentLower, err := os.ReadFile(path.Join(parentDir, lowerFile))
12121162
if err == nil {
@@ -1267,24 +1217,15 @@ func (d *Driver) getLowerDirs(id string) ([]string, error) {
12671217
for s := range strings.SplitSeq(string(lowers), ":") {
12681218
lower := d.dir(s)
12691219
lp, err := os.Readlink(lower)
1270-
// if the link does not exist, we lost the symlinks during a sudden reboot.
1271-
// Let's go ahead and recreate those symlinks.
12721220
if err != nil {
1273-
if os.IsNotExist(err) {
1274-
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)
1275-
if err := d.recreateSymlinks(); err != nil {
1276-
return nil, fmt.Errorf("recreating the missing symlinks: %w", err)
1277-
}
1278-
// let's call Readlink on lower again now that we have recreated the missing symlinks
1279-
lp, err = os.Readlink(lower)
1280-
if err != nil {
1281-
return nil, err
1282-
}
1283-
} else {
1284-
return nil, err
1221+
if errors.Is(err, syscall.EINVAL) {
1222+
// Not a symlink: new format, lower is already the diff path.
1223+
lowersArray = append(lowersArray, lower)
1224+
continue
12851225
}
1226+
return nil, err
12861227
}
1287-
lowersArray = append(lowersArray, path.Clean(d.dir(path.Join("link", lp))))
1228+
lowersArray = append(lowersArray, d.dir(path.Join("link", lp)))
12881229
}
12891230
} else if !os.IsNotExist(err) {
12901231
return nil, err
@@ -1394,112 +1335,6 @@ func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
13941335
return t.Cleanup, nil
13951336
}
13961337

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

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

storage/drivers/overlay/randomid.go

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

0 commit comments

Comments
 (0)