github.com/openflowlabs/storage@v1.12.13/drivers/overlay/check.go (about) 1 // +build linux 2 3 package overlay 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path" 10 "path/filepath" 11 "syscall" 12 13 "github.com/containers/storage/pkg/ioutils" 14 "github.com/containers/storage/pkg/mount" 15 "github.com/containers/storage/pkg/system" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 "golang.org/x/sys/unix" 19 ) 20 21 // doesSupportNativeDiff checks whether the filesystem has a bug 22 // which copies up the opaque flag when copying up an opaque 23 // directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR. 24 // When these exist naive diff should be used. 25 func doesSupportNativeDiff(d, mountOpts string) error { 26 td, err := ioutil.TempDir(d, "opaque-bug-check") 27 if err != nil { 28 return err 29 } 30 defer func() { 31 if err := os.RemoveAll(td); err != nil { 32 logrus.Warnf("Failed to remove check directory %v: %v", td, err) 33 } 34 }() 35 36 // Make directories l1/d, l1/d1, l2/d, l3, work, merged 37 if err := os.MkdirAll(filepath.Join(td, "l1", "d"), 0755); err != nil { 38 return err 39 } 40 if err := os.MkdirAll(filepath.Join(td, "l1", "d1"), 0755); err != nil { 41 return err 42 } 43 if err := os.MkdirAll(filepath.Join(td, "l2", "d"), 0755); err != nil { 44 return err 45 } 46 if err := os.Mkdir(filepath.Join(td, "l3"), 0755); err != nil { 47 return err 48 } 49 if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil { 50 return err 51 } 52 if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil { 53 return err 54 } 55 56 // Mark l2/d as opaque 57 if err := system.Lsetxattr(filepath.Join(td, "l2", "d"), "trusted.overlay.opaque", []byte("y"), 0); err != nil { 58 return errors.Wrap(err, "failed to set opaque flag on middle layer") 59 } 60 61 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, "work")) 62 flags, data := mount.ParseOptions(mountOpts) 63 if data != "" { 64 opts = fmt.Sprintf("%s,%s", opts, data) 65 } 66 if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", uintptr(flags), opts); err != nil { 67 return errors.Wrap(err, "failed to mount overlay") 68 } 69 defer func() { 70 if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { 71 logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) 72 } 73 }() 74 75 // Touch file in d to force copy up of opaque directory "d" from "l2" to "l3" 76 if err := ioutil.WriteFile(filepath.Join(td, "merged", "d", "f"), []byte{}, 0644); err != nil { 77 return errors.Wrap(err, "failed to write to merged directory") 78 } 79 80 // Check l3/d does not have opaque flag 81 xattrOpaque, err := system.Lgetxattr(filepath.Join(td, "l3", "d"), "trusted.overlay.opaque") 82 if err != nil { 83 return errors.Wrap(err, "failed to read opaque flag on upper layer") 84 } 85 if string(xattrOpaque) == "y" { 86 return errors.New("opaque flag erroneously copied up, consider update to kernel 4.8 or later to fix") 87 } 88 89 // rename "d1" to "d2" 90 if err := os.Rename(filepath.Join(td, "merged", "d1"), filepath.Join(td, "merged", "d2")); err != nil { 91 // if rename failed with syscall.EXDEV, the kernel doesn't have CONFIG_OVERLAY_FS_REDIRECT_DIR enabled 92 if err.(*os.LinkError).Err == syscall.EXDEV { 93 return nil 94 } 95 return errors.Wrap(err, "failed to rename dir in merged directory") 96 } 97 // get the xattr of "d2" 98 xattrRedirect, err := system.Lgetxattr(filepath.Join(td, "l3", "d2"), "trusted.overlay.redirect") 99 if err != nil { 100 return errors.Wrap(err, "failed to read redirect flag on upper layer") 101 } 102 103 if string(xattrRedirect) == "d1" { 104 return errors.New("kernel has CONFIG_OVERLAY_FS_REDIRECT_DIR enabled") 105 } 106 107 return nil 108 } 109 110 // doesMetacopy checks if the filesystem is going to optimize changes to 111 // metadata by using nodes marked with an "overlay.metacopy" attribute to avoid 112 // copying up a file from a lower layer unless/until its contents are being 113 // modified 114 func doesMetacopy(d, mountOpts string) (bool, error) { 115 td, err := ioutil.TempDir(d, "metacopy-check") 116 if err != nil { 117 return false, err 118 } 119 defer func() { 120 if err := os.RemoveAll(td); err != nil { 121 logrus.Warnf("Failed to remove check directory %v: %v", td, err) 122 } 123 }() 124 125 // Make directories l1, l2, work, merged 126 if err := os.MkdirAll(filepath.Join(td, "l1"), 0755); err != nil { 127 return false, err 128 } 129 if err := ioutils.AtomicWriteFile(filepath.Join(td, "l1", "f"), []byte{0xff}, 0700); err != nil { 130 return false, err 131 } 132 if err := os.MkdirAll(filepath.Join(td, "l2"), 0755); err != nil { 133 return false, err 134 } 135 if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil { 136 return false, err 137 } 138 if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil { 139 return false, err 140 } 141 // Mount using the mandatory options and configured options 142 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", path.Join(td, "l1"), path.Join(td, "l2"), path.Join(td, "work")) 143 flags, data := mount.ParseOptions(mountOpts) 144 if data != "" { 145 opts = fmt.Sprintf("%s,%s", opts, data) 146 } 147 if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", uintptr(flags), opts); err != nil { 148 return false, errors.Wrap(err, "failed to mount overlay for metacopy check") 149 } 150 defer func() { 151 if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { 152 logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) 153 } 154 }() 155 // Make a change that only impacts the inode, and check if the pulled-up copy is marked 156 // as a metadata-only copy 157 if err := os.Chmod(filepath.Join(td, "merged", "f"), 0600); err != nil { 158 return false, errors.Wrap(err, "error changing permissions on file for metacopy check") 159 } 160 metacopy, err := system.Lgetxattr(filepath.Join(td, "l2", "f"), "trusted.overlay.metacopy") 161 if err != nil { 162 return false, errors.Wrap(err, "metacopy flag was not set on file in upper layer") 163 } 164 return metacopy != nil, nil 165 }