github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/persistence/problems/pack_deer.go (about) 1 package problems 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "encoding/binary" 7 "github.com/LanceLRQ/deer-common/constants" 8 "github.com/LanceLRQ/deer-common/persistence" 9 commonStructs "github.com/LanceLRQ/deer-common/structs" 10 "github.com/LanceLRQ/deer-common/utils" 11 "github.com/pkg/errors" 12 uuid "github.com/satori/go.uuid" 13 "io" 14 "os" 15 "path" 16 "path/filepath" 17 "strings" 18 ) 19 20 func mergeFilesBinary(options *persistence.ProblemPackageOptions) (string, error) { 21 tmpFileName := uuid.NewV1().String() + ".zip" 22 tmpFilePath := path.Join("/tmp/", tmpFileName) 23 zipFile, err := os.Create(tmpFilePath) 24 if err != nil { 25 return "", err 26 } 27 defer zipFile.Close() 28 zipWriter := zip.NewWriter(zipFile) 29 defer zipWriter.Close() 30 31 err = filepath.Walk(options.ConfigDir, func(zpath string, info os.FileInfo, err error) error { 32 if err != nil { 33 return err 34 } 35 // 跳过配置文件 36 if zpath == options.ConfigFile { 37 return nil 38 } 39 // 跳过配置文件夹 40 if zpath == options.ConfigDir { 41 return nil 42 } 43 // 跳过二进制文件夹 44 binDir := path.Join(options.ConfigDir, "bin") 45 if strings.HasPrefix(zpath, binDir) { 46 return nil 47 } 48 header, err := zip.FileInfoHeader(info) 49 if err != nil { 50 return err 51 } 52 header.Name = strings.TrimPrefix(zpath, options.ConfigDir+"/") 53 if info.IsDir() { 54 header.Name += "/" 55 } else { 56 header.Method = zip.Deflate 57 } 58 writer, err := zipWriter.CreateHeader(header) 59 if err != nil { 60 return err 61 } 62 if !info.IsDir() { 63 file, err := os.Open(zpath) 64 if err != nil { 65 return err 66 } 67 defer file.Close() 68 _, err = io.Copy(writer, file) 69 } 70 return err 71 }) 72 if err != nil { 73 return "", err 74 } 75 return tmpFilePath, nil 76 } 77 78 func writeFileHeader(writer io.Writer, pack ProblemPackage) error { 79 buf16 := make([]byte, 2) 80 buf32 := make([]byte, 4) 81 82 // magic 83 binary.BigEndian.PutUint16(buf16, constants.ProblemPackageMagicCode) 84 if _, err := writer.Write(buf16); err != nil { 85 return errors.Errorf("write problem file error: %s", err.Error()) 86 } 87 88 // Version 89 binary.BigEndian.PutUint16(buf16, pack.Version) 90 if _, err := writer.Write(buf16); err != nil { 91 return errors.Errorf("write problem file error: %s", err.Error()) 92 } 93 94 // Commit Version 95 binary.BigEndian.PutUint32(buf32, pack.CommitVersion) 96 if _, err := writer.Write(buf32); err != nil { 97 return errors.Errorf("write problem file error: %s", err.Error()) 98 } 99 100 // ConfigSize 101 binary.BigEndian.PutUint32(buf32, pack.ConfigSize) 102 if _, err := writer.Write(buf32); err != nil { 103 return errors.Errorf("write problem file error: %s", err.Error()) 104 } 105 106 // BodySize 107 binary.BigEndian.PutUint32(buf32, pack.BodySize) 108 if _, err := writer.Write(buf32); err != nil { 109 return errors.Errorf("write problem file error: %s", err.Error()) 110 } 111 112 // CertSize 113 binary.BigEndian.PutUint16(buf16, pack.CertSize) 114 if _, err := writer.Write(buf16); err != nil { 115 return errors.Errorf("write problem file error: %s", err.Error()) 116 } 117 118 // Certificate 119 if pack.CertSize > 0 { 120 if _, err := writer.Write(pack.Certificate); err != nil { 121 return errors.Errorf("write problem file error: %s", err.Error()) 122 } 123 } 124 125 return nil 126 } 127 128 // 执行题目数据表打包操作 129 func PackProblems( 130 configuration *commonStructs.JudgeConfiguration, 131 options *persistence.ProblemPackageOptions, 132 ) error { 133 134 if options.DigitalSign { 135 if options.DigitalPEM.PublicKey == nil || options.DigitalPEM.PrivateKey == nil { 136 return errors.Errorf("digital sign need public key and private key") 137 } 138 } 139 140 fout, err := os.Create(options.OutFile) 141 if err != nil { 142 return errors.Errorf("create problem package file error: %s", err.Error()) 143 } 144 defer fout.Close() 145 146 configBytes := utils.ObjectToJSONByte(configuration) 147 148 bodyFile, err := mergeFilesBinary(options) 149 if err != nil { 150 return err 151 } 152 153 bodyInfo, err := os.Stat(bodyFile) 154 if err != nil { 155 return err 156 } 157 158 certSize := 0 // 0 means disable cert 159 var publicKeyRaw []byte 160 if options.DigitalSign { 161 certSize = len(options.DigitalPEM.PublicKeyRaw) 162 publicKeyRaw = options.DigitalPEM.PublicKeyRaw 163 } 164 165 pack := ProblemPackage{ 166 Version: 1, 167 CommitVersion: 1, 168 Configs: configBytes, 169 ConfigSize: uint32(len(configBytes)), 170 BodySize: uint32(bodyInfo.Size()), 171 CertSize: uint16(certSize), 172 Certificate: publicKeyRaw, 173 } 174 // Write Header 175 err = writeFileHeader(fout, pack) 176 if err != nil { 177 return err 178 } 179 180 // Write Signature 181 fBody, err := os.Open(bodyFile) 182 if err != nil { 183 return err 184 } 185 186 hash, err := persistence.SHA256Streams([]io.Reader{ 187 bytes.NewReader(configBytes), 188 fBody, 189 }) 190 if err != nil { 191 return err 192 } 193 _ = fBody.Close() 194 // GPG signature 195 if options.DigitalSign { 196 hash, err = persistence.RSA2048Sign(hash, options.DigitalPEM.PrivateKey) 197 if err != nil { 198 return err 199 } 200 } 201 buf16 := make([]byte, 2) 202 signSize := uint16(len(hash)) 203 // SignSize 204 binary.BigEndian.PutUint16(buf16, signSize) 205 if _, err := fout.Write(buf16); err != nil { 206 return errors.Errorf("write problem file error: %s", err.Error()) 207 } 208 // Signature 209 if _, err := fout.Write(hash); err != nil { 210 return errors.Errorf("write problem file error: %s", err.Error()) 211 } 212 213 // Write configs and Body 214 // 要注意先写入configs,再写body,方便后续校验的时候直接顺序读取 215 if _, err := fout.Write(pack.Configs); err != nil { 216 return errors.Errorf("write problem file error: %s", err.Error()) 217 } 218 fBody, err = os.Open(bodyFile) 219 if err != nil { 220 return err 221 } 222 defer fBody.Close() 223 // Copy Body to fout 224 if _, err := io.Copy(fout, fBody); err != nil { 225 return errors.Errorf("write problem file error: %s", err.Error()) 226 } 227 228 // Clean 229 _ = os.Remove(bodyFile) 230 231 return nil 232 233 }