github.com/auxten/ginkgo@v0.0.0-20220130172820-7d98ad59d232/seed/seed.go (about) 1 package seed 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "hash/crc32" 7 "hash/fnv" 8 "net/netip" 9 "os" 10 "sync" 11 "time" 12 13 "github.com/auxten/ginkgo/srcdest" 14 ) 15 16 var ( 17 DefaultBlockSize int64 = 4 * 1024 * 1024 18 //DefaultBlockSize int64 = 4 * 1024 19 ) 20 21 type Seed struct { 22 sync.RWMutex 23 Path string `json:"path"` 24 FileCount int `json:"fileCount"` 25 Files []*File `json:"files"` 26 Blocks []*Block `json:"blocks"` 27 BlockSize int64 `json:"blockSize"` 28 VNodeCount uint8 `json:"vnodeCount"` 29 // Hosts are only updated before Marshal 30 Hosts []Host `json:"hosts"` 31 //InitFileIdx int `json:"initFileIdx"` 32 //InitBlockIdx int `json:"initBlockIdx"` 33 TotalSize int64 `json:"totalSize"` 34 TotalWritten int64 `json:"-"` 35 //TmpSize int64 `json:"tmpSize"` 36 //LastInitBlock int64 `json:"lastInitBlock"` 37 } 38 39 type File struct { 40 mtime time.Time 41 Mode os.FileMode `json:"mode"` 42 //Size of file, -1 for dir, -2 for symbol link 43 Size int64 `json:"size"` 44 //SymPath if symbol link target path 45 SymPath string `json:"symPath"` 46 Path string `json:"path"` 47 LocalPath string `json:"-"` 48 CheckSum []byte `json:"checkSum"` 49 } 50 51 type Host struct { 52 IP [4]byte `json:"ip"` // IPv4 only 53 Port uint16 `json:"port"` 54 } 55 56 type HostPath struct { 57 Host string `json:"host" form:"host" query:"host"` 58 Path string `json:"path" form:"path" query:"path"` 59 } 60 61 type Block struct { 62 Size int64 `json:"size"` 63 StartFile int `json:"startFile"` 64 StartOffset int64 `json:"startOffset"` 65 Done bool `json:"-"` 66 CheckSum []byte `json:"checkSum"` 67 Hosts map[Host]time.Time `json:"-"` 68 } 69 70 func (h Host) String() string { 71 return fmt.Sprintf("%d.%d.%d.%d:%d", h.IP[0], h.IP[1], h.IP[2], h.IP[3], h.Port) 72 } 73 74 // ParseHost parses "IPv4:Port" 75 func ParseHost(hStr string) (Host, error) { 76 if ipPort, err := netip.ParseAddrPort(hStr); err != nil { 77 return Host{}, err 78 } else { 79 if !ipPort.Addr().Is4() { 80 return Host{}, fmt.Errorf("only IPv4 addresses") 81 } 82 ipBytes, _ := ipPort.Addr().MarshalBinary() 83 return Host{ 84 IP: [4]byte{ipBytes[0], ipBytes[1], ipBytes[2], ipBytes[3]}, 85 Port: ipPort.Port(), 86 }, nil 87 } 88 } 89 90 // Hash uses HashCrc which has better uniformity 91 func (h Host) Hash(vIndex byte) uint32 { 92 return h.HashCrc(vIndex) 93 } 94 95 func (h Host) HashFnv(vIndex byte) uint32 { 96 hash := fnv.New32a() 97 _, _ = hash.Write(h.IP[:]) 98 _, _ = hash.Write([]byte{byte(h.Port / 256), byte(h.Port % 256), vIndex}) 99 return hash.Sum32() 100 } 101 102 func (h Host) HashCrc(vIndex byte) uint32 { 103 hash := crc32.New(crc32.MakeTable(crc32.Castagnoli)) 104 _, _ = hash.Write(h.IP[:]) 105 _, _ = hash.Write([]byte{byte(h.Port / 256), byte(h.Port % 256), vIndex}) 106 return hash.Sum32() 107 } 108 109 func (h Host) HashSha(vIndex byte) uint32 { 110 hash := sha256.New() 111 _, _ = hash.Write(h.IP[:]) 112 _, _ = hash.Write([]byte{byte(h.Port / 256), byte(h.Port % 256), vIndex}) 113 hout := fnv.New32a() 114 hout.Write(hash.Sum(nil)) 115 return hout.Sum32() 116 } 117 118 func (sd *Seed) Localize(cmdSrcPath string, cmdDestPath string) (err error) { 119 var ( 120 srcType srcdest.PathType 121 destType srcdest.PathType 122 fInfo os.FileInfo 123 ) 124 if sd.Files[0].Size >= 0 { 125 srcType = srcdest.FileType 126 } else if sd.Files[0].Size == -1 { 127 srcType = srcdest.DirType 128 } else { 129 return fmt.Errorf("src root path type %d is not supported", sd.Files[0].Size) 130 } 131 132 if fInfo, err = os.Stat(cmdDestPath); err != nil { 133 if os.IsNotExist(err) { 134 destType = srcdest.NotExist 135 } else { 136 return 137 } 138 } else if fInfo.IsDir() { 139 destType = srcdest.DirType 140 } else if fInfo.Mode().IsRegular() { 141 destType = srcdest.FileType 142 } else { 143 return fmt.Errorf("dest path type %s is not supported", fInfo.Mode().Type().String()) 144 } 145 146 for i := range sd.Files { 147 sd.Files[i].LocalPath, err = srcdest.NormalizeDestPath(cmdSrcPath, cmdDestPath, srcType, destType, sd.Files[i].Path) 148 if err != nil { 149 return 150 } 151 } 152 153 return 154 } 155 156 // TouchAll make all directory and all files including symbol links and empty files. 157 // THIS Must be called after Localize 158 func (sd *Seed) TouchAll() (err error) { 159 for _, file := range sd.Files { 160 if file.Size >= 0 { 161 var fd *os.File 162 fd, err = os.OpenFile(file.LocalPath, os.O_CREATE, file.Mode.Perm()|0200) 163 if err != nil && !os.IsExist(err) { 164 return 165 } else { 166 if err = fd.Close(); err != nil { 167 return 168 } 169 } 170 } else if file.Size == -1 { 171 if err = os.MkdirAll(file.LocalPath, file.Mode.Perm()|0700); err != nil { 172 return 173 } 174 175 } else if file.Size == -2 { 176 err = os.Symlink(file.SymPath, file.LocalPath) 177 if err != nil && !os.IsExist(err) { 178 return 179 } 180 } 181 } 182 return 183 }