// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
TarWithOptions creates an archive from the directory at `path`, only including files whose relative
paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
// TarWithOptions creates an archive from the directory at ` path ` only including files whose relative // paths are included in ` options . IncludeFiles ` ( if non - nil ) or not in ` options . ExcludePatterns ` .
func tarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
        srcPath = fixVolumePathPrefix(srcPath)
        pm, err := newPatternMatcher(options.ExcludePatterns)
        if err != nil {
                return nil, err
        }
        pipeReader, pipeWriter := io.Pipe()
        compressWriter, err := compressStream(pipeWriter, options.Compression)
        if err != nil {
                return nil, err
        }
        go func() {
                ta := newTarAppender(
                        NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
                        compressWriter,
                )
                ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat)
                defer func() {
                        if err := ta.TarWriter.Close(); err != nil {
                                logrus.Errorf("Can't close tar writer: %s", err)
                        }
                        if err := compressWriter.Close(); err != nil {
                                logrus.Errorf("Can't close compress writer: %s", err)
                        }
                        if err := pipeWriter.Close(); err != nil {
                                logrus.Errorf("Can't close pipe writer: %s", err)
                        }
                }()
                defer BufioWriter32KPool.Put(ta.Buffer)
                stat, err := os.Lstat(srcPath)
                if err != nil {
                        return
                }
                if !stat.IsDir() {
                        if len(options.IncludeFiles) > 0 {
                                logrus.Warn("Tar: Can't archive a file with includes")
                        }
                        dir, base := splitPathDirEntry(srcPath)
                        srcPath = dir
                        options.IncludeFiles = []string{base}
                }
                if len(options.IncludeFiles) == 0 {
                        options.IncludeFiles = []string{"."}
                }
                seen := make(map[string]bool)
                for _, include := range options.IncludeFiles {
                        rebaseName := options.RebaseNames[include]
                        walkRoot := getWalkRoot(srcPath, include)
                        filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
                                if err != nil {
                                        logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err)
                                        return nil
                                }
                                relFilePath, err := filepath.Rel(srcPath, filePath)
                                if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
                                        return nil
                                }
                                if options.IncludeSourceDir && include == "." && relFilePath != "." {
                                        relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
                                }
                                skip := false
                                if include != relFilePath {
                                        skip, err = pm.Matches(relFilePath)
                                        if err != nil {
                                                logrus.Errorf("Error matching %s: %v", relFilePath, err)
                                                return err
                                        }
                                }
                                if skip {
                                        if !f.IsDir() {
                                                return nil
                                        }
                                        if !pm.Exclusions() {
                                                return filepath.SkipDir
                                        }
                                        dirSlash := relFilePath + string(filepath.Separator)
                                        for _, pat := range pm.Patterns() {
                                                if !pat.Exclusion() {
                                                        continue
                                                }
                                                if strings.HasPrefix(pat.String() + string(filepath.Separator), dirSlash) {
                                                        return nil
                                                }
                                        }
                                        return filepath.SkipDir
                                }
                                if seen[relFilePath] {
                                        return nil
                                }
                                seen[relFilePath] = true
                                if rebaseName != "" {
                                        var replacement string
                                        if rebaseName != string(filepath.Separator) {
                                                replacement = rebaseName
                                        }
                                        relFilePath = strings.Replace(relFilePath, include, replacement, 1)
                                }
                                if err := ta.addTarFile(filePath, relFilePath); err != nil {
                                        logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
                                        if err == io.ErrClosedPipe {
                                                return err
                                        }
                                }
                                return nil
                        })
                }
        }()
        return pipeReader, nil
}
func tarWithOptions ( srcPath string , options * TarOptions ) ( io . ReadCloser , error ) { srcPath = fixVolumePathPrefix ( srcPath ) pm , err := newPatternMatcher ( options . ExcludePatterns ) if err != nil { return nil , err } pipeReader , pipeWriter := io . Pipe ( ) compressWriter , err := compressStream ( pipeWriter , options . Compression ) if err != nil { return nil , err } go func ( ) { ta := newTarAppender ( NewIDMappingsFromMaps ( options . UIDMaps , options . GIDMaps ) , compressWriter , ) ta . WhiteoutConverter = getWhiteoutConverter ( options . WhiteoutFormat ) defer func ( ) { if err := ta . TarWriter . Close ( ) ; err != nil { logrus . Errorf ( " " , err ) } if err := compressWriter . Close ( ) ; err != nil { logrus . Errorf ( " " , err ) } if err := pipeWriter . Close ( ) ; err != nil { logrus . Errorf ( " " , err ) } } ( ) defer BufioWriter32KPool . Put ( ta . Buffer ) stat , err := os . Lstat ( srcPath ) if err != nil { return } if ! stat . IsDir ( ) { if len ( options . IncludeFiles ) > 0 { logrus . Warn ( " " ) } dir , base := splitPathDirEntry ( srcPath ) srcPath = dir options . IncludeFiles = [ ] string { base } } if len ( options . IncludeFiles ) == 0 { options . IncludeFiles = [ ] string { " " } } seen := make ( map [ string ] bool ) for _ , include := range options . IncludeFiles { rebaseName := options . RebaseNames [ include ] walkRoot := getWalkRoot ( srcPath , include ) filepath . Walk ( walkRoot , func ( filePath string , f os . FileInfo , err error ) error { if err != nil { logrus . Errorf ( " " , srcPath , err ) return nil } relFilePath , err := filepath . Rel ( srcPath , filePath ) if err != nil || ( ! options . IncludeSourceDir && relFilePath == " " && f . IsDir ( ) ) { return nil } if options . IncludeSourceDir && include == " " && relFilePath != " " { relFilePath = strings . Join ( [ ] string { " " , relFilePath } , string ( filepath . Separator ) ) } skip := false if include != relFilePath { skip , err = pm . Matches ( relFilePath ) if err != nil { logrus . Errorf ( " " , relFilePath , err ) return err } } if skip { if ! f . IsDir ( ) { return nil } if ! pm . Exclusions ( ) { return filepath . SkipDir } dirSlash := relFilePath + string ( filepath . Separator ) for _ , pat := range pm . Patterns ( ) { if ! pat . Exclusion ( ) { continue } if strings . HasPrefix ( pat . String ( ) + string ( filepath . Separator ) , dirSlash ) { return nil } } return filepath . SkipDir } if seen [ relFilePath ] { return nil } seen [ relFilePath ] = true if rebaseName != " " { var replacement string if rebaseName != string ( filepath . Separator ) { replacement = rebaseName } relFilePath = strings . Replace ( relFilePath , include , replacement , 1 ) } if err := ta . addTarFile ( filePath , relFilePath ) ; err != nil { logrus . Errorf ( " " , filePath , err ) if err == io . ErrClosedPipe { return err } } return nil } ) } } ( ) return pipeReader , nil }
TarWithOptions creates an archive from the directory at `path`, only including files whose relative
paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
TarWithOptions creates an archive from the directory at ` path ` only including files whose relative paths are included in ` options . IncludeFiles ` ( if non - nil ) or not in ` options . ExcludePatterns ` .
// Fix the source path to work with long path names. This is a no-op // on platforms other than Windows. // Make sure to check the error on Close. // this buffer is needed for the duration of this piped stream // In general we log errors here but ignore them because // during e.g. a diff operation the container can continue // mutating the filesystem and we can see transient errors // from this // We can't later join a non-dir with any includes because the // 'walk' will error if "file/." is stat-ed and "file" is not a // directory. So, we must split the source path and use the // basename as the include. // Error getting relative path OR we are looking // at the source directory path. Skip in both situations. // If "include" is an exact match for the current file // then even if there's an "excludePatterns" pattern that // matches it, don't skip it. IOW, assume an explicit 'include' // is asking for that file no matter what - which is true // for some files, like .dockerignore and Dockerfile (sometimes) // If we want to skip this file and its a directory // then we should first check to see if there's an // excludes pattern (e.g. !dir/file) that starts with this // dir. If so then we can't skip this dir. // Its not a dir then so we can just return/skip. // No exceptions (!...) in patterns so just skip dir // found a match - so can't skip this dir // No matching exclusion dir so just skip dir // Rename the base resource. // Special case the root directory to replace with an // empty string instead so that we don't end up with // double slashes in the paths. // if pipe is broken, stop writing tar stream to it