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  }