github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/filesystem/util/computedFiles.go (about) 1 package util 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "path" 10 "sort" 11 "strings" 12 13 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 14 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 15 ) 16 17 func spliceComputedFiles(fs *filesystem.FileSystem, 18 computedFileList []ComputedFile) error { 19 if len(computedFileList) < 1 { 20 return nil 21 } 22 filenameToInodeTable := fs.FilenameToInodeTable() 23 inodeToFilenamesTable := fs.InodeToFilenamesTable() 24 for _, computedFile := range computedFileList { 25 inum, ok := filenameToInodeTable[computedFile.Filename] 26 if !ok { 27 return errors.New(computedFile.Filename + ": missing from image") 28 } 29 if filenames, ok := inodeToFilenamesTable[inum]; !ok { 30 panic(computedFile.Filename + ": no corresponding list of files") 31 } else if len(filenames) != 1 { 32 return fmt.Errorf("%s: multiple inodes: %d", computedFile.Filename, 33 len(filenames)) 34 } 35 if inode, ok := 36 fs.InodeTable[inum].(*filesystem.ComputedRegularInode); ok { 37 inode.Source = computedFile.Source 38 continue 39 } 40 if oldInode, ok := fs.InodeTable[inum].(*filesystem.RegularInode); !ok { 41 return fmt.Errorf("%s: type: %T is not a regular inode", 42 computedFile.Filename, fs.InodeTable[inum]) 43 } else { 44 newInode := new(filesystem.ComputedRegularInode) 45 newInode.Mode = oldInode.Mode 46 newInode.Uid = oldInode.Uid 47 newInode.Gid = oldInode.Gid 48 newInode.Source = computedFile.Source 49 fs.InodeTable[inum] = newInode 50 } 51 } 52 fs.ComputeTotalDataBytes() 53 clearInodePointers(&fs.DirectoryInode, "") 54 return fs.RebuildInodePointers() 55 } 56 57 func loadComputedFiles(filename string) ([]ComputedFile, error) { 58 var computedFileList []ComputedFile 59 if strings.HasSuffix(filename, ".json") { 60 file, err := os.Open(filename) 61 if err != nil { 62 return nil, err 63 } 64 defer file.Close() 65 reader := bufio.NewReader(file) 66 decoder := json.NewDecoder(reader) 67 if err := decoder.Decode(&computedFileList); err != nil { 68 return nil, errors.New("error decoding computed files list " + 69 err.Error()) 70 } 71 } else { 72 lines, err := fsutil.LoadLines(filename) 73 if err != nil { 74 return nil, err 75 } 76 for _, line := range lines { 77 fields := strings.Fields(line) 78 if len(fields) != 2 { 79 return nil, fmt.Errorf("bad line: %s", line) 80 } 81 computedFileList = append(computedFileList, 82 ComputedFile{fields[0], fields[1]}) 83 } 84 } 85 return computedFileList, nil 86 } 87 88 func clearInodePointers(directoryInode *filesystem.DirectoryInode, 89 name string) { 90 for _, dirent := range directoryInode.EntryList { 91 if inode, ok := dirent.Inode().(*filesystem.DirectoryInode); ok { 92 clearInodePointers(inode, path.Join(name, dirent.Name)) 93 } 94 dirent.SetInode(nil) 95 } 96 } 97 98 func mergeComputedFiles(base, overlay []ComputedFile) []ComputedFile { 99 computedFilesMap := make(map[string]string) 100 for _, computedFile := range base { 101 computedFilesMap[computedFile.Filename] = computedFile.Source 102 } 103 for _, computedFile := range overlay { 104 computedFilesMap[computedFile.Filename] = computedFile.Source 105 } 106 filenames := make([]string, 0, len(computedFilesMap)) 107 for filename := range computedFilesMap { 108 filenames = append(filenames, filename) 109 } 110 sort.Strings(filenames) 111 newList := make([]ComputedFile, 0, len(computedFilesMap)) 112 for _, filename := range filenames { 113 newList = append(newList, ComputedFile{ 114 Filename: filename, 115 Source: computedFilesMap[filename], 116 }) 117 } 118 return newList 119 }