github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/filesystem/tar/encode.go (about) 1 package tar 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io" 7 "syscall" 8 "time" 9 10 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 11 "github.com/Cloud-Foundations/Dominator/lib/hash" 12 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 13 ) 14 15 func encode(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 16 objectsGetter objectserver.ObjectsGetter) error { 17 hashList := getOrderedObjectsList(fileSystem) 18 objectsReader, err := objectsGetter.GetObjects(hashList) 19 if err != nil { 20 return err 21 } 22 defer objectsReader.Close() 23 return writeDirectory(tarWriter, fileSystem, &fileSystem.DirectoryInode, 24 ".", objectsReader, make(map[uint64]struct{})) 25 } 26 27 func getOrderedObjectsList(fileSystem *filesystem.FileSystem) []hash.Hash { 28 hashList := make([]hash.Hash, 0, len(fileSystem.InodeTable)) 29 inodeTable := make(map[uint64]struct{}, len(fileSystem.InodeTable)) 30 listObjects(&fileSystem.DirectoryInode, &hashList, inodeTable) 31 return hashList 32 } 33 34 func listObjects(inode *filesystem.DirectoryInode, hashList *[]hash.Hash, 35 inodeTable map[uint64]struct{}) { 36 for _, entry := range inode.EntryList { 37 if eInode, ok := entry.Inode().(*filesystem.RegularInode); ok { 38 if eInode.Size > 0 { 39 if _, ok := inodeTable[entry.InodeNumber]; !ok { 40 *hashList = append(*hashList, eInode.Hash) 41 inodeTable[entry.InodeNumber] = struct{}{} 42 } 43 } 44 } else if eInode, ok := entry.Inode().(*filesystem.DirectoryInode); ok { 45 listObjects(eInode, hashList, inodeTable) 46 } 47 } 48 } 49 50 func writeDirectory(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 51 inode *filesystem.DirectoryInode, dirname string, 52 objectsReader objectserver.ObjectsReader, 53 inodeTable map[uint64]struct{}) error { 54 header := tar.Header{ 55 Name: dirname + "/", 56 Mode: int64(inode.Mode), 57 Uid: int(inode.Uid), 58 Gid: int(inode.Gid), 59 Typeflag: tar.TypeDir, 60 } 61 if err := tarWriter.WriteHeader(&header); err != nil { 62 return err 63 } 64 for _, entry := range inode.EntryList { 65 err := writeInode(tarWriter, fileSystem, entry.Inode(), 66 dirname+"/"+entry.Name, entry.InodeNumber, objectsReader, 67 inodeTable) 68 if err != nil { 69 return err 70 } 71 } 72 return nil 73 } 74 75 func writeRegularFile(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 76 inode *filesystem.RegularInode, name string, inodeNumber uint64, 77 objectsReader objectserver.ObjectsReader, 78 inodeTable map[uint64]struct{}) error { 79 header := tar.Header{ 80 Name: name, 81 Mode: int64(inode.Mode), 82 Uid: int(inode.Uid), 83 Gid: int(inode.Gid), 84 Size: int64(inode.Size), 85 ModTime: time.Unix(inode.MtimeSeconds, int64(inode.MtimeNanoSeconds)), 86 Typeflag: tar.TypeReg, 87 } 88 err := writeHeader(tarWriter, fileSystem, &header, inodeNumber, 89 inodeTable) 90 if err != nil { 91 return err 92 } 93 if header.Size > 0 { 94 size, reader, err := objectsReader.NextObject() 95 if err != nil { 96 return err 97 } 98 defer reader.Close() 99 if size != inode.Size { 100 return fmt.Errorf("%s inode size: %d, object size: %d", 101 name, inode.Size, size) 102 } 103 nCopied, err := io.Copy(tarWriter, reader) 104 if err != nil { 105 return err 106 } 107 if nCopied != int64(size) { 108 return fmt.Errorf("nCopied: %d != size: %d", nCopied, size) 109 } 110 } 111 return nil 112 } 113 114 func writeHeader(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 115 header *tar.Header, inum uint64, inodeTable map[uint64]struct{}) error { 116 if _, ok := inodeTable[inum]; ok { 117 header.Linkname = "." + fileSystem.InodeToFilenamesTable()[inum][0] 118 header.Size = 0 119 header.Typeflag = tar.TypeLink 120 } else { 121 inodeTable[inum] = struct{}{} 122 } 123 return tarWriter.WriteHeader(header) 124 } 125 126 func writeInode(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 127 inode filesystem.GenericInode, name string, inodeNumber uint64, 128 objectsReader objectserver.ObjectsReader, 129 inodeTable map[uint64]struct{}) error { 130 var err error 131 if eInode, ok := inode.(*filesystem.DirectoryInode); ok { 132 err = writeDirectory(tarWriter, fileSystem, eInode, name, objectsReader, 133 inodeTable) 134 } else if eInode, ok := inode.(*filesystem.RegularInode); ok { 135 err = writeRegularFile(tarWriter, fileSystem, eInode, name, inodeNumber, 136 objectsReader, inodeTable) 137 } else if eInode, ok := inode.(*filesystem.ComputedRegularInode); ok { 138 err = writeRegularFile(tarWriter, fileSystem, &filesystem.RegularInode{ 139 Mode: eInode.Mode, 140 Uid: eInode.Uid, 141 Gid: eInode.Gid, 142 }, name, inodeNumber, objectsReader, inodeTable) 143 } else if eInode, ok := inode.(*filesystem.SpecialInode); ok { 144 err = writeSpecial(tarWriter, fileSystem, eInode, name, inodeNumber, 145 inodeTable) 146 } else if eInode, ok := inode.(*filesystem.SymlinkInode); ok { 147 err = writeSymlink(tarWriter, fileSystem, eInode, name, inodeNumber, 148 inodeTable) 149 } 150 return err 151 } 152 153 func writeSpecial(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 154 inode *filesystem.SpecialInode, name string, inodeNumber uint64, 155 inodeTable map[uint64]struct{}) error { 156 header := tar.Header{ 157 Name: name, 158 Mode: int64(inode.Mode), 159 Uid: int(inode.Uid), 160 Gid: int(inode.Gid), 161 ModTime: time.Unix(inode.MtimeSeconds, int64(inode.MtimeNanoSeconds)), 162 Devmajor: int64(inode.Rdev >> 8), 163 Devminor: int64(inode.Rdev & 0xff), 164 } 165 if inode.Mode&syscall.S_IFMT == syscall.S_IFCHR { 166 header.Typeflag = tar.TypeChar 167 } else if inode.Mode&syscall.S_IFMT == syscall.S_IFBLK { 168 header.Typeflag = tar.TypeBlock 169 } else if inode.Mode&syscall.S_IFMT == syscall.S_IFIFO { 170 header.Typeflag = tar.TypeFifo 171 } else { 172 return fmt.Errorf("unsupported inode mode: %d", inode.Mode) 173 } 174 return writeHeader(tarWriter, fileSystem, &header, inodeNumber, inodeTable) 175 } 176 177 func writeSymlink(tarWriter *tar.Writer, fileSystem *filesystem.FileSystem, 178 inode *filesystem.SymlinkInode, name string, inodeNumber uint64, 179 inodeTable map[uint64]struct{}) error { 180 header := tar.Header{ 181 Name: name, 182 Mode: 0777, 183 Uid: int(inode.Uid), 184 Gid: int(inode.Gid), 185 Typeflag: tar.TypeSymlink, 186 Linkname: inode.Symlink, 187 } 188 return writeHeader(tarWriter, fileSystem, &header, inodeNumber, inodeTable) 189 }