@@ -516,21 +516,44 @@ func (cc *cacheContext) includedPaths(ctx context.Context, m *mount, p string, o
516516 txn := cc .tree .Txn ()
517517 root = txn .Root ()
518518 var (
519- updated bool
520- iter * iradix.Iterator
521- k []byte
522- kOk bool
519+ updated bool
520+ iter * iradix.Iterator
521+ k []byte
522+ kOk bool
523+ origPrefix string
524+ resolvedPrefix string
523525 )
524526
525527 iter = root .Iterator ()
526528
527529 if opts .Wildcard {
528- k , _ , kOk = iter .Next ()
530+ origPrefix , k , kOk , err = wildcardPrefix (root , p )
531+ if err != nil {
532+ return nil , err
533+ }
529534 } else {
530- k = convertPathToKey ([]byte (p ))
531- if _ , kOk = root .Get (k ); kOk {
535+ origPrefix = p
536+ k = convertPathToKey ([]byte (origPrefix ))
537+
538+ // We need to resolve symlinks here, in case the base path
539+ // involves a symlink. That will match fsutil behavior of
540+ // calling functions such as stat and walk.
541+ var cr * CacheRecord
542+ k , cr , err = getFollowLinks (root , k , true )
543+ if err != nil {
544+ return nil , err
545+ }
546+ kOk = (cr != nil )
547+ }
548+
549+ if origPrefix != "" {
550+ if kOk {
532551 iter .SeekLowerBound (append (append ([]byte {}, k ... ), 0 ))
533552 }
553+
554+ resolvedPrefix = string (convertKeyToPath (k ))
555+ } else {
556+ k , _ , kOk = iter .Next ()
534557 }
535558
536559 var (
@@ -541,6 +564,20 @@ func (cc *cacheContext) includedPaths(ctx context.Context, m *mount, p string, o
541564 for kOk {
542565 fn := string (convertKeyToPath (k ))
543566
567+ // Convert the path prefix from what we found in the prefix
568+ // tree to what the argument specified.
569+ //
570+ // For example, if the original 'p' argument was /a/b and there
571+ // is a symlink a->c, we want fn to be /a/b/foo rather than
572+ // /c/b/foo. This is necessary to ensure correct pattern
573+ // matching.
574+ //
575+ // When wildcards are enabled, this translation applies to the
576+ // portion of 'p' before any wildcards.
577+ if strings .HasPrefix (fn , resolvedPrefix ) {
578+ fn = origPrefix + strings .TrimPrefix (fn , resolvedPrefix )
579+ }
580+
544581 for len (parentDirHeaders ) != 0 {
545582 lastParentDir := parentDirHeaders [len (parentDirHeaders )- 1 ]
546583 if strings .HasPrefix (fn , lastParentDir .path + "/" ) {
@@ -591,8 +628,7 @@ func (cc *cacheContext) includedPaths(ctx context.Context, m *mount, p string, o
591628 }
592629 } else {
593630 if ! strings .HasPrefix (fn + "/" , p + "/" ) {
594- k , _ , kOk = iter .Next ()
595- continue
631+ break
596632 }
597633
598634 shouldInclude , err = shouldIncludePath (
@@ -692,6 +728,82 @@ func shouldIncludePath(
692728 return true , nil
693729}
694730
731+ func wildcardPrefix (root * iradix.Node , p string ) (string , []byte , bool , error ) {
732+ // For consistency with what the copy implementation in fsutil
733+ // does: split pattern into non-wildcard prefix and rest of
734+ // pattern, then follow symlinks when resolving the non-wildcard
735+ // prefix.
736+
737+ d1 , d2 := splitWildcards (p )
738+ if d1 == "/" {
739+ return "" , nil , false , nil
740+ }
741+
742+ linksWalked := 0
743+ k , cr , err := getFollowLinksWalk (root , convertPathToKey ([]byte (d1 )), true , & linksWalked )
744+ if err != nil {
745+ return "" , k , false , err
746+ }
747+
748+ if d2 != "" && cr != nil && cr .Type == CacheRecordTypeSymlink {
749+ // getFollowLinks only handles symlinks in path
750+ // components before the last component, so
751+ // handle last component in d1 specially.
752+ resolved := string (convertKeyToPath (k ))
753+ for {
754+ v , ok := root .Get (k )
755+
756+ if ! ok {
757+ return d1 , k , false , nil
758+ }
759+ if v .(* CacheRecord ).Type != CacheRecordTypeSymlink {
760+ break
761+ }
762+
763+ linksWalked ++
764+ if linksWalked > 255 {
765+ return "" , k , false , errors .Errorf ("too many links" )
766+ }
767+
768+ resolved := cleanLink (resolved , v .(* CacheRecord ).Linkname )
769+ k = convertPathToKey ([]byte (resolved ))
770+ }
771+ }
772+ return d1 , k , cr != nil , nil
773+ }
774+
775+ func splitWildcards (p string ) (d1 , d2 string ) {
776+ parts := strings .Split (path .Join (p ), "/" )
777+ var p1 , p2 []string
778+ var found bool
779+ for _ , p := range parts {
780+ if ! found && containsWildcards (p ) {
781+ found = true
782+ }
783+ if p == "" {
784+ p = "/"
785+ }
786+ if ! found {
787+ p1 = append (p1 , p )
788+ } else {
789+ p2 = append (p2 , p )
790+ }
791+ }
792+ return filepath .Join (p1 ... ), filepath .Join (p2 ... )
793+ }
794+
795+ func containsWildcards (name string ) bool {
796+ for i := 0 ; i < len (name ); i ++ {
797+ ch := name [i ]
798+ if ch == '\\' {
799+ i ++
800+ } else if ch == '*' || ch == '?' || ch == '[' {
801+ return true
802+ }
803+ }
804+ return false
805+ }
806+
695807func (cc * cacheContext ) checksumNoFollow (ctx context.Context , m * mount , p string ) (* CacheRecord , error ) {
696808 p = keyPath (p )
697809
@@ -973,14 +1085,8 @@ func getFollowLinksWalk(root *iradix.Node, k []byte, follow bool, linksWalked *i
9731085 if * linksWalked > 255 {
9741086 return nil , nil , errors .Errorf ("too many links" )
9751087 }
976- dirPath := path .Clean (string (convertKeyToPath (dir )))
977- if dirPath == "." || dirPath == "/" {
978- dirPath = ""
979- }
980- link := path .Clean (parent .Linkname )
981- if ! path .IsAbs (link ) {
982- link = path .Join ("/" , path .Join (path .Dir (dirPath ), link ))
983- }
1088+
1089+ link := cleanLink (string (convertKeyToPath (dir )), parent .Linkname )
9841090 return getFollowLinksWalk (root , append (convertPathToKey ([]byte (link )), file ... ), follow , linksWalked )
9851091 }
9861092 }
@@ -992,6 +1098,18 @@ func getFollowLinksWalk(root *iradix.Node, k []byte, follow bool, linksWalked *i
9921098 return k , nil , nil
9931099}
9941100
1101+ func cleanLink (dir , linkname string ) string {
1102+ dirPath := path .Clean (dir )
1103+ if dirPath == "." || dirPath == "/" {
1104+ dirPath = ""
1105+ }
1106+ link := path .Clean (linkname )
1107+ if ! path .IsAbs (link ) {
1108+ return path .Join ("/" , path .Join (path .Dir (dirPath ), link ))
1109+ }
1110+ return link
1111+ }
1112+
9951113func prepareDigest (fp , p string , fi os.FileInfo ) (digest.Digest , error ) {
9961114 h , err := NewFileHash (fp , fi )
9971115 if err != nil {
0 commit comments