github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/daemon/graphdriver/overlay2/check.go (about) 1 //go:build linux 2 // +build linux 3 4 package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" 5 6 import ( 7 "fmt" 8 "os" 9 "path" 10 "path/filepath" 11 "syscall" 12 13 "github.com/containerd/containerd/sys" 14 "github.com/docker/docker/pkg/system" 15 "github.com/pkg/errors" 16 "golang.org/x/sys/unix" 17 ) 18 19 // doesSupportNativeDiff checks whether the filesystem has a bug 20 // which copies up the opaque flag when copying up an opaque 21 // directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR. 22 // When these exist naive diff should be used. 23 // 24 // When running in a user namespace, returns errRunningInUserNS 25 // immediately. 26 func doesSupportNativeDiff(d string) error { 27 if sys.RunningInUserNS() { 28 return errors.New("running in a user namespace") 29 } 30 31 td, err := os.MkdirTemp(d, "opaque-bug-check") 32 if err != nil { 33 return err 34 } 35 defer func() { 36 if err := os.RemoveAll(td); err != nil { 37 logger.Warnf("Failed to remove check directory %v: %v", td, err) 38 } 39 }() 40 41 // Make directories l1/d, l1/d1, l2/d, l3, work, merged 42 if err := os.MkdirAll(filepath.Join(td, "l1", "d"), 0755); err != nil { 43 return err 44 } 45 if err := os.MkdirAll(filepath.Join(td, "l1", "d1"), 0755); err != nil { 46 return err 47 } 48 if err := os.MkdirAll(filepath.Join(td, "l2", "d"), 0755); err != nil { 49 return err 50 } 51 if err := os.Mkdir(filepath.Join(td, "l3"), 0755); err != nil { 52 return err 53 } 54 if err := os.Mkdir(filepath.Join(td, workDirName), 0755); err != nil { 55 return err 56 } 57 if err := os.Mkdir(filepath.Join(td, mergedDirName), 0755); err != nil { 58 return err 59 } 60 61 // Mark l2/d as opaque 62 if err := system.Lsetxattr(filepath.Join(td, "l2", "d"), "trusted.overlay.opaque", []byte("y"), 0); err != nil { 63 return errors.Wrap(err, "failed to set opaque flag on middle layer") 64 } 65 66 opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, workDirName)) 67 if err := unix.Mount("overlay", filepath.Join(td, mergedDirName), "overlay", 0, opts); err != nil { 68 return errors.Wrap(err, "failed to mount overlay") 69 } 70 defer func() { 71 if err := unix.Unmount(filepath.Join(td, mergedDirName), 0); err != nil { 72 logger.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, mergedDirName), err) 73 } 74 }() 75 76 // Touch file in d to force copy up of opaque directory "d" from "l2" to "l3" 77 if err := os.WriteFile(filepath.Join(td, mergedDirName, "d", "f"), []byte{}, 0644); err != nil { 78 return errors.Wrap(err, "failed to write to merged directory") 79 } 80 81 // Check l3/d does not have opaque flag 82 xattrOpaque, err := system.Lgetxattr(filepath.Join(td, "l3", "d"), "trusted.overlay.opaque") 83 if err != nil { 84 return errors.Wrap(err, "failed to read opaque flag on upper layer") 85 } 86 if string(xattrOpaque) == "y" { 87 return errors.New("opaque flag erroneously copied up, consider update to kernel 4.8 or later to fix") 88 } 89 90 // rename "d1" to "d2" 91 if err := os.Rename(filepath.Join(td, mergedDirName, "d1"), filepath.Join(td, mergedDirName, "d2")); err != nil { 92 // if rename failed with syscall.EXDEV, the kernel doesn't have CONFIG_OVERLAY_FS_REDIRECT_DIR enabled 93 if err.(*os.LinkError).Err == syscall.EXDEV { 94 return nil 95 } 96 return errors.Wrap(err, "failed to rename dir in merged directory") 97 } 98 // get the xattr of "d2" 99 xattrRedirect, err := system.Lgetxattr(filepath.Join(td, "l3", "d2"), "trusted.overlay.redirect") 100 if err != nil { 101 return errors.Wrap(err, "failed to read redirect flag on upper layer") 102 } 103 104 if string(xattrRedirect) == "d1" { 105 return errors.New("kernel has CONFIG_OVERLAY_FS_REDIRECT_DIR enabled") 106 } 107 108 return nil 109 }