@@ -10,6 +10,8 @@ import (
1010 "path/filepath"
1111 "syscall"
1212
13+ "github.com/containers/storage/pkg/ioutils"
14+ "github.com/containers/storage/pkg/mount"
1315 "github.com/containers/storage/pkg/system"
1416 "github.com/pkg/errors"
1517 "github.com/sirupsen/logrus"
@@ -57,10 +59,11 @@ func doesSupportNativeDiff(d, mountOpts string) error {
5759 }
5860
5961 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" ))
60- if mountOpts != "" {
61- opts = fmt .Sprintf ("%s,%s" , opts , mountOpts )
62+ flags , data := mount .ParseOptions (mountOpts )
63+ if data != "" {
64+ opts = fmt .Sprintf ("%s,%s" , opts , data )
6265 }
63- if err := unix .Mount ("overlay" , filepath .Join (td , "merged" ), "overlay" , 0 , opts ); err != nil {
66+ if err := unix .Mount ("overlay" , filepath .Join (td , "merged" ), "overlay" , uintptr ( flags ) , opts ); err != nil {
6467 return errors .Wrap (err , "failed to mount overlay" )
6568 }
6669 defer func () {
@@ -103,3 +106,60 @@ func doesSupportNativeDiff(d, mountOpts string) error {
103106
104107 return nil
105108}
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+ }
0 commit comments