github.com/Cloud-Foundations/Dominator@v0.3.4/lib/filesystem/untar/decode.go (about) 1 package untar 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io" 7 "path" 8 "strings" 9 "syscall" 10 11 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 12 "github.com/Cloud-Foundations/Dominator/lib/filter" 13 ) 14 15 type decoderData struct { 16 nextInodeNumber uint64 17 fileSystem filesystem.FileSystem 18 inodeTable map[string]uint64 19 directoryTable map[string]*filesystem.DirectoryInode 20 } 21 22 func decode(tarReader *tar.Reader, hasher Hasher, filter *filter.Filter) ( 23 *filesystem.FileSystem, error) { 24 var decoderData decoderData 25 decoderData.inodeTable = make(map[string]uint64) 26 decoderData.directoryTable = make(map[string]*filesystem.DirectoryInode) 27 fileSystem := &decoderData.fileSystem 28 fileSystem.InodeTable = make(filesystem.InodeTable) 29 // Create a default top-level directory which may be updated. 30 decoderData.addInode("/", &fileSystem.DirectoryInode) 31 fileSystem.DirectoryInode.Mode = syscall.S_IFDIR | syscall.S_IRWXU | 32 syscall.S_IRGRP | syscall.S_IXGRP | syscall.S_IROTH | syscall.S_IXOTH 33 decoderData.directoryTable["/"] = &fileSystem.DirectoryInode 34 for { 35 header, err := tarReader.Next() 36 if err == io.EOF { 37 break 38 } 39 if err != nil { 40 return nil, err 41 } 42 header.Name = normaliseFilename(header.Name) 43 if header.Name == "/.subd" || 44 strings.HasPrefix(header.Name, "/.subd/") { 45 continue 46 } 47 if filter != nil && filter.Match(header.Name) { 48 continue 49 } 50 err = decoderData.addHeader(tarReader, hasher, header) 51 if err != nil { 52 return nil, err 53 } 54 } 55 delete(fileSystem.InodeTable, 0) 56 fileSystem.DirectoryCount = uint64(len(decoderData.directoryTable)) 57 fileSystem.ComputeTotalDataBytes() 58 sortDirectory(&fileSystem.DirectoryInode) 59 return fileSystem, nil 60 } 61 62 func normaliseFilename(filename string) string { 63 if filename[:2] == "./" { 64 filename = filename[1:] 65 } else if filename[0] != '/' { 66 filename = "/" + filename 67 } 68 length := len(filename) 69 if length > 1 && filename[length-1] == '/' { 70 filename = filename[:length-1] 71 } 72 return filename 73 } 74 75 func (decoderData *decoderData) addHeader(tarReader *tar.Reader, hasher Hasher, 76 header *tar.Header) error { 77 parentDir, ok := decoderData.directoryTable[path.Dir(header.Name)] 78 if !ok { 79 return fmt.Errorf("no parent directory found for: %s", header.Name) 80 } 81 leafName := path.Base(header.Name) 82 if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA { 83 return decoderData.addRegularFile(tarReader, hasher, header, 84 parentDir, leafName) 85 } else if header.Typeflag == tar.TypeLink { 86 return decoderData.addHardlink(header, parentDir, leafName) 87 } else if header.Typeflag == tar.TypeSymlink { 88 return decoderData.addSymlink(header, parentDir, leafName) 89 } else if header.Typeflag == tar.TypeChar { 90 return decoderData.addSpecialFile(header, parentDir, leafName) 91 } else if header.Typeflag == tar.TypeBlock { 92 return decoderData.addSpecialFile(header, parentDir, leafName) 93 } else if header.Typeflag == tar.TypeDir { 94 return decoderData.addDirectory(header, parentDir, leafName) 95 } else if header.Typeflag == tar.TypeFifo { 96 return decoderData.addSpecialFile(header, parentDir, leafName) 97 } else { 98 return fmt.Errorf("unsupported file type: %v", header.Typeflag) 99 } 100 } 101 102 func (decoderData *decoderData) addRegularFile(tarReader *tar.Reader, 103 hasher Hasher, header *tar.Header, parent *filesystem.DirectoryInode, 104 name string) error { 105 var newInode filesystem.RegularInode 106 newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) | 107 syscall.S_IFREG) 108 newInode.Uid = uint32(header.Uid) 109 newInode.Gid = uint32(header.Gid) 110 newInode.MtimeNanoSeconds = int32(header.ModTime.Nanosecond()) 111 newInode.MtimeSeconds = header.ModTime.Unix() 112 newInode.Size = uint64(header.Size) 113 if header.Size > 0 { 114 var err error 115 newInode.Hash, err = hasher.Hash(tarReader, uint64(header.Size)) 116 if err != nil { 117 return err 118 } 119 } 120 decoderData.addEntry(parent, header.Name, name, &newInode) 121 return nil 122 } 123 124 func (decoderData *decoderData) addDirectory(header *tar.Header, 125 parent *filesystem.DirectoryInode, name string) error { 126 var newInode filesystem.DirectoryInode 127 newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) | 128 syscall.S_IFDIR) 129 newInode.Uid = uint32(header.Uid) 130 newInode.Gid = uint32(header.Gid) 131 if header.Name == "/" { 132 *decoderData.directoryTable[header.Name] = newInode 133 return nil 134 } 135 decoderData.addEntry(parent, header.Name, name, &newInode) 136 decoderData.directoryTable[header.Name] = &newInode 137 return nil 138 } 139 140 func (decoderData *decoderData) addHardlink(header *tar.Header, 141 parent *filesystem.DirectoryInode, name string) error { 142 header.Linkname = normaliseFilename(header.Linkname) 143 if inum, ok := decoderData.inodeTable[header.Linkname]; ok { 144 var newEntry filesystem.DirectoryEntry 145 newEntry.Name = name 146 newEntry.InodeNumber = inum 147 parent.EntryList = append(parent.EntryList, &newEntry) 148 } else { 149 return fmt.Errorf("missing hardlink target: %s", header.Linkname) 150 } 151 return nil 152 } 153 154 func (decoderData *decoderData) addSymlink(header *tar.Header, 155 parent *filesystem.DirectoryInode, name string) error { 156 var newInode filesystem.SymlinkInode 157 newInode.Uid = uint32(header.Uid) 158 newInode.Gid = uint32(header.Gid) 159 newInode.Symlink = header.Linkname 160 decoderData.addEntry(parent, header.Name, name, &newInode) 161 return nil 162 } 163 164 func (decoderData *decoderData) addSpecialFile(header *tar.Header, 165 parent *filesystem.DirectoryInode, name string) error { 166 var newInode filesystem.SpecialInode 167 if header.Typeflag == tar.TypeChar { 168 newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) | 169 syscall.S_IFCHR) 170 } else if header.Typeflag == tar.TypeBlock { 171 newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) | 172 syscall.S_IFBLK) 173 } else if header.Typeflag == tar.TypeFifo { 174 newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) | 175 syscall.S_IFIFO) 176 } else { 177 return fmt.Errorf("unsupported type: %v", header.Typeflag) 178 } 179 newInode.Uid = uint32(header.Uid) 180 newInode.Gid = uint32(header.Gid) 181 newInode.MtimeNanoSeconds = int32(header.ModTime.Nanosecond()) 182 newInode.MtimeSeconds = header.ModTime.Unix() 183 if header.Devminor > 255 { 184 return fmt.Errorf("minor device number: %d too large", 185 header.Devminor) 186 } 187 newInode.Rdev = uint64(header.Devmajor<<8 | header.Devminor) 188 decoderData.addEntry(parent, header.Name, name, &newInode) 189 return nil 190 } 191 192 func (decoderData *decoderData) addEntry(parent *filesystem.DirectoryInode, 193 fullName, name string, inode filesystem.GenericInode) { 194 var newEntry filesystem.DirectoryEntry 195 newEntry.Name = name 196 newEntry.InodeNumber = decoderData.nextInodeNumber 197 newEntry.SetInode(inode) 198 parent.EntryList = append(parent.EntryList, &newEntry) 199 decoderData.addInode(fullName, inode) 200 } 201 202 func (decoderData *decoderData) addInode(fullName string, 203 inode filesystem.GenericInode) { 204 decoderData.inodeTable[fullName] = decoderData.nextInodeNumber 205 decoderData.fileSystem.InodeTable[decoderData.nextInodeNumber] = inode 206 decoderData.nextInodeNumber++ 207 }