github.com/Cloud-Foundations/Dominator@v0.3.4/lib/filesystem/util/replaceComputedFiles.go (about) 1 package util 2 3 import ( 4 "bytes" 5 "crypto/sha512" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "time" 13 14 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 15 "github.com/Cloud-Foundations/Dominator/lib/hash" 16 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 17 ) 18 19 type combinedObjectsGetter struct { 20 filenameToHash map[string]hash.Hash 21 hashToData map[hash.Hash][]byte 22 hashToFile map[hash.Hash]string 23 hashToSize map[hash.Hash]uint64 24 objectsGetter objectserver.ObjectsGetter 25 } 26 27 type combinedObjectsReader struct { 28 hashes []hash.Hash 29 objectsGetter *combinedObjectsGetter 30 objectsReader objectserver.ObjectsReader 31 } 32 33 func replaceComputedFiles(fs *filesystem.FileSystem, 34 computedFilesData *ComputedFilesData, 35 objectsGetter objectserver.ObjectsGetter) ( 36 objectserver.ObjectsGetter, error) { 37 newObjectsGetter, err := makeCombinedObjectsGetter(computedFilesData, 38 objectsGetter) 39 if err != nil { 40 return nil, err 41 } 42 err = replaceInDirectory(fs, &fs.DirectoryInode, "/", newObjectsGetter) 43 if err != nil { 44 return nil, err 45 } 46 return newObjectsGetter, nil 47 } 48 49 func replaceInDirectory(fs *filesystem.FileSystem, 50 directory *filesystem.DirectoryInode, dirname string, 51 objectsGetter *combinedObjectsGetter) error { 52 for _, entry := range directory.EntryList { 53 gInode := entry.Inode() 54 if inode, ok := gInode.(*filesystem.DirectoryInode); ok { 55 err := replaceInDirectory(fs, inode, path.Join(dirname, entry.Name), 56 objectsGetter) 57 if err != nil { 58 return err 59 } 60 } else if inode, ok := gInode.(*filesystem.ComputedRegularInode); ok { 61 filename := path.Join(dirname, entry.Name) 62 hashVal, ok := objectsGetter.filenameToHash[filename] 63 if !ok { 64 return fmt.Errorf("missing computed file: %s", filename) 65 } 66 fInode := &filesystem.RegularInode{ 67 Mode: inode.Mode, 68 Uid: inode.Uid, 69 Gid: inode.Gid, 70 MtimeSeconds: time.Now().Unix(), 71 Size: objectsGetter.hashToSize[hashVal], 72 Hash: hashVal, 73 } 74 entry.SetInode(fInode) 75 fs.InodeTable[entry.InodeNumber] = fInode 76 } 77 } 78 return nil 79 } 80 81 func makeCombinedObjectsGetter(computedFilesData *ComputedFilesData, 82 objectsGetter objectserver.ObjectsGetter) (*combinedObjectsGetter, error) { 83 newObjectsGetter := &combinedObjectsGetter{ 84 filenameToHash: make(map[string]hash.Hash), 85 hashToData: make(map[hash.Hash][]byte), 86 hashToFile: make(map[hash.Hash]string), 87 hashToSize: make(map[hash.Hash]uint64), 88 objectsGetter: objectsGetter, 89 } 90 for filename, data := range computedFilesData.FileData { 91 checksummer := sha512.New() 92 if _, err := checksummer.Write(data); err != nil { 93 return nil, err 94 } 95 var hashVal hash.Hash 96 copy(hashVal[:], checksummer.Sum(nil)) 97 newObjectsGetter.filenameToHash[filename] = hashVal 98 newObjectsGetter.hashToData[hashVal] = data 99 newObjectsGetter.hashToSize[hashVal] = uint64(len(data)) 100 } 101 if computedFilesData.RootDirectory != "" { 102 err := newObjectsGetter.scanDirectory(computedFilesData.RootDirectory, 103 "/") 104 if err != nil { 105 return nil, err 106 } 107 } 108 return newObjectsGetter, nil 109 } 110 111 func (objectsGetter *combinedObjectsGetter) GetObjects(hashes []hash.Hash) ( 112 objectserver.ObjectsReader, error) { 113 unknownHashes := make([]hash.Hash, 0, len(hashes)) 114 for _, hashVal := range hashes { 115 if _, ok := objectsGetter.hashToData[hashVal]; ok { 116 continue 117 } 118 if _, ok := objectsGetter.hashToFile[hashVal]; ok { 119 continue 120 } 121 unknownHashes = append(unknownHashes, hashVal) 122 } 123 objectsReader, err := objectsGetter.objectsGetter.GetObjects(unknownHashes) 124 if err != nil { 125 return nil, err 126 } 127 return &combinedObjectsReader{hashes, objectsGetter, objectsReader}, nil 128 } 129 130 func (objectsGetter *combinedObjectsGetter) scanDirectory(realDir string, 131 mapDir string) error { 132 file, err := os.Open(realDir) 133 if err != nil { 134 return err 135 } 136 names, err := file.Readdirnames(-1) 137 file.Close() 138 if err != nil { 139 return err 140 } 141 for _, name := range names { 142 realPath := path.Join(realDir, name) 143 mapPath := path.Join(mapDir, name) 144 if err := objectsGetter.scanEntry(realPath, mapPath); err != nil { 145 return err 146 } 147 } 148 return nil 149 } 150 151 func (objectsGetter *combinedObjectsGetter) scanEntry(realFilename string, 152 mapFilename string) error { 153 if fi, err := os.Lstat(realFilename); err != nil { 154 return err 155 } else if fi.IsDir() { 156 return objectsGetter.scanDirectory(realFilename, mapFilename) 157 } else if fi.Mode()&os.ModeType != 0 { 158 return errors.New(realFilename + ": is not a directory or regular file") 159 } 160 if file, err := os.Open(realFilename); err != nil { 161 return err 162 } else { 163 defer file.Close() 164 checksummer := sha512.New() 165 nWritten, err := io.Copy(checksummer, file) 166 if err != nil { 167 return err 168 } 169 var hashVal hash.Hash 170 copy(hashVal[:], checksummer.Sum(nil)) 171 objectsGetter.filenameToHash[mapFilename] = hashVal 172 objectsGetter.hashToFile[hashVal] = realFilename 173 objectsGetter.hashToSize[hashVal] = uint64(nWritten) 174 } 175 return nil 176 } 177 178 func (objectsReader *combinedObjectsReader) Close() error { 179 return objectsReader.objectsReader.Close() 180 } 181 182 func (objectsReader *combinedObjectsReader) NextObject() ( 183 uint64, io.ReadCloser, error) { 184 hashVal := objectsReader.hashes[0] 185 objectsReader.hashes = objectsReader.hashes[1:] 186 if data, ok := objectsReader.objectsGetter.hashToData[hashVal]; ok { 187 return objectsReader.objectsGetter.hashToSize[hashVal], 188 ioutil.NopCloser(bytes.NewReader(data)), nil 189 } 190 if filename, ok := objectsReader.objectsGetter.hashToFile[hashVal]; ok { 191 if file, err := os.Open(filename); err != nil { 192 return 0, nil, err 193 } else { 194 return objectsReader.objectsGetter.hashToSize[hashVal], file, nil 195 } 196 } 197 return objectsReader.objectsReader.NextObject() 198 }