github.com/psexton/git-lfs@v2.1.1-0.20170517224304-289a18b2bc53+incompatible/lfs/pointer_smudge.go (about)

     1  package lfs
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/cheggaaa/pb"
    10  	"github.com/git-lfs/git-lfs/tools"
    11  	"github.com/git-lfs/git-lfs/tq"
    12  
    13  	"github.com/git-lfs/git-lfs/config"
    14  	"github.com/git-lfs/git-lfs/errors"
    15  	"github.com/git-lfs/git-lfs/progress"
    16  	"github.com/rubyist/tracerx"
    17  )
    18  
    19  func PointerSmudgeToFile(filename string, ptr *Pointer, download bool, manifest *tq.Manifest, cb progress.CopyCallback) error {
    20  	os.MkdirAll(filepath.Dir(filename), 0755)
    21  	file, err := os.Create(filename)
    22  	if err != nil {
    23  		return fmt.Errorf("Could not create working directory file: %v", err)
    24  	}
    25  	defer file.Close()
    26  	if err := PointerSmudge(file, ptr, filename, download, manifest, cb); err != nil {
    27  		if errors.IsDownloadDeclinedError(err) {
    28  			// write placeholder data instead
    29  			file.Seek(0, os.SEEK_SET)
    30  			ptr.Encode(file)
    31  			return err
    32  		} else {
    33  			return fmt.Errorf("Could not write working directory file: %v", err)
    34  		}
    35  	}
    36  	return nil
    37  }
    38  
    39  func PointerSmudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, manifest *tq.Manifest, cb progress.CopyCallback) error {
    40  	mediafile, err := LocalMediaPath(ptr.Oid)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	LinkOrCopyFromReference(ptr.Oid, ptr.Size)
    46  
    47  	stat, statErr := os.Stat(mediafile)
    48  	if statErr == nil && stat != nil {
    49  		fileSize := stat.Size()
    50  		if fileSize == 0 || fileSize != ptr.Size {
    51  			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
    52  			os.RemoveAll(mediafile)
    53  			stat = nil
    54  		}
    55  	}
    56  
    57  	if statErr != nil || stat == nil {
    58  		if download {
    59  			err = downloadFile(writer, ptr, workingfile, mediafile, manifest, cb)
    60  		} else {
    61  			return errors.NewDownloadDeclinedError(statErr, "smudge")
    62  		}
    63  	} else {
    64  		err = readLocalFile(writer, ptr, mediafile, workingfile, cb)
    65  	}
    66  
    67  	if err != nil {
    68  		return errors.NewSmudgeError(err, ptr.Oid, mediafile)
    69  	}
    70  
    71  	return nil
    72  }
    73  
    74  func downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, manifest *tq.Manifest, cb progress.CopyCallback) error {
    75  	fmt.Fprintf(os.Stderr, "Downloading %s (%s)\n", workingfile, pb.FormatBytes(ptr.Size))
    76  
    77  	q := tq.NewTransferQueue(tq.Download, manifest, "")
    78  	q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size)
    79  	q.Wait()
    80  
    81  	if errs := q.Errors(); len(errs) > 0 {
    82  		var multiErr error
    83  		for _, e := range errs {
    84  			if multiErr != nil {
    85  				multiErr = fmt.Errorf("%v\n%v", multiErr, e)
    86  			} else {
    87  				multiErr = e
    88  			}
    89  			return errors.Wrapf(multiErr, "Error downloading %s (%s)", workingfile, ptr.Oid)
    90  		}
    91  	}
    92  
    93  	return readLocalFile(writer, ptr, mediafile, workingfile, nil)
    94  }
    95  
    96  func readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb progress.CopyCallback) error {
    97  	reader, err := os.Open(mediafile)
    98  	if err != nil {
    99  		return errors.Wrapf(err, "Error opening media file.")
   100  	}
   101  	defer reader.Close()
   102  
   103  	if ptr.Size == 0 {
   104  		if stat, _ := os.Stat(mediafile); stat != nil {
   105  			ptr.Size = stat.Size()
   106  		}
   107  	}
   108  
   109  	if len(ptr.Extensions) > 0 {
   110  		registeredExts := config.Config.Extensions()
   111  		extensions := make(map[string]config.Extension)
   112  		for _, ptrExt := range ptr.Extensions {
   113  			ext, ok := registeredExts[ptrExt.Name]
   114  			if !ok {
   115  				err := fmt.Errorf("Extension '%s' is not configured.", ptrExt.Name)
   116  				return errors.Wrap(err, "smudge")
   117  			}
   118  			ext.Priority = ptrExt.Priority
   119  			extensions[ext.Name] = ext
   120  		}
   121  		exts, err := config.SortExtensions(extensions)
   122  		if err != nil {
   123  			return errors.Wrap(err, "smudge")
   124  		}
   125  
   126  		// pipe extensions in reverse order
   127  		var extsR []config.Extension
   128  		for i := range exts {
   129  			ext := exts[len(exts)-1-i]
   130  			extsR = append(extsR, ext)
   131  		}
   132  
   133  		request := &pipeRequest{"smudge", reader, workingfile, extsR}
   134  
   135  		response, err := pipeExtensions(request)
   136  		if err != nil {
   137  			return errors.Wrap(err, "smudge")
   138  		}
   139  
   140  		actualExts := make(map[string]*pipeExtResult)
   141  		for _, result := range response.results {
   142  			actualExts[result.name] = result
   143  		}
   144  
   145  		// verify name, order, and oids
   146  		oid := response.results[0].oidIn
   147  		if ptr.Oid != oid {
   148  			err = fmt.Errorf("Actual oid %s during smudge does not match expected %s", oid, ptr.Oid)
   149  			return errors.Wrap(err, "smudge")
   150  		}
   151  
   152  		for _, expected := range ptr.Extensions {
   153  			actual := actualExts[expected.Name]
   154  			if actual.name != expected.Name {
   155  				err = fmt.Errorf("Actual extension name '%s' does not match expected '%s'", actual.name, expected.Name)
   156  				return errors.Wrap(err, "smudge")
   157  			}
   158  			if actual.oidOut != expected.Oid {
   159  				err = fmt.Errorf("Actual oid %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid)
   160  				return errors.Wrap(err, "smudge")
   161  			}
   162  		}
   163  
   164  		// setup reader
   165  		reader, err = os.Open(response.file.Name())
   166  		if err != nil {
   167  			return errors.Wrapf(err, "Error opening smudged file: %s", err)
   168  		}
   169  		defer reader.Close()
   170  	}
   171  
   172  	_, err = tools.CopyWithCallback(writer, reader, ptr.Size, cb)
   173  	if err != nil {
   174  		return errors.Wrapf(err, "Error reading from media file: %s", err)
   175  	}
   176  
   177  	return nil
   178  }