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 }