github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/persistence/judge_result/pack.go (about) 1 package judge_result 2 3 import ( 4 "bytes" 5 "compress/gzip" 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 "io/ioutil" 15 "os" 16 "path" 17 ) 18 19 func readAndWriteToTempFile(writer io.Writer, fileName string, workDir string) error { 20 buf16 := make([]byte, 16) 21 buf32 := make([]byte, 4) 22 body, err := ioutil.ReadFile(path.Join(workDir, fileName)) 23 if err != nil { 24 return err 25 } 26 binary.BigEndian.PutUint16(buf16, constants.JudgeBodyPackageMagicCode) 27 binary.BigEndian.PutUint32(buf32, uint32(len(body))) 28 if _, err := writer.Write(buf16); err != nil { 29 return errors.Errorf("write temp file error: %s", err.Error()) 30 } 31 if _, err := writer.Write(buf32); err != nil { 32 return errors.Errorf("write temp file error: %s", err.Error()) 33 } 34 if _, err := writer.Write([]byte(fileName + "\n")); err != nil { 35 return errors.Errorf("write temp file error: %s", err.Error()) 36 } 37 if _, err := writer.Write(body); err != nil { 38 return errors.Errorf("write temp file error: %s", err.Error()) 39 } 40 return nil 41 } 42 43 func mergeResultBinary( 44 options *persistence.JudgeResultPersisOptions, 45 judgeResult *commonStructs.JudgeResult, 46 ) (string, error) { 47 tmpFileName := uuid.NewV1().String() + ".tmp" 48 tmpFilePath := path.Join("/tmp/", tmpFileName) 49 var testCaseWriter io.Writer 50 tmpFile, err := os.OpenFile(tmpFilePath, os.O_CREATE|os.O_WRONLY, 0644) 51 if err != nil { 52 return "", errors.Errorf("create temp file error: %s", err.Error()) 53 } 54 defer tmpFile.Close() 55 if options.CompressorType == 1 { // GZIP 56 zipWriter := gzip.NewWriter(tmpFile) 57 testCaseWriter = zipWriter 58 defer zipWriter.Close() 59 } else { 60 testCaseWriter = tmpFile 61 } 62 63 for _, testCase := range judgeResult.TestCases { 64 // 如果不需要保留AC的数据 65 if !options.SaveAcceptedData && testCase.JudgeResult == constants.JudgeFlagAC { 66 continue 67 } 68 err = readAndWriteToTempFile(testCaseWriter, testCase.ProgramOut, options.SessionDir) 69 _ = readAndWriteToTempFile(testCaseWriter, testCase.ProgramError, options.SessionDir) 70 _ = readAndWriteToTempFile(testCaseWriter, testCase.CheckerOut, options.SessionDir) 71 _ = readAndWriteToTempFile(testCaseWriter, testCase.CheckerError, options.SessionDir) 72 _ = readAndWriteToTempFile(testCaseWriter, testCase.CheckerReport, options.SessionDir) 73 } 74 75 return tmpFilePath, nil 76 } 77 78 func writeFileHeaderAndResult(writer io.Writer, pack JudgeResultPackage) error { 79 buf8 := make([]byte, 1) 80 buf16 := make([]byte, 2) 81 buf32 := make([]byte, 4) 82 83 // magic 84 binary.BigEndian.PutUint16(buf16, constants.JudgeResultMagicCode) 85 if _, err := writer.Write(buf16); err != nil { 86 return errors.Errorf("write result file error: %s", err.Error()) 87 } 88 89 // Version 90 buf8[0] = pack.Version 91 if _, err := writer.Write(buf8); err != nil { 92 return errors.Errorf("write result file error: %s", err.Error()) 93 } 94 95 // CompressorType 96 buf8[0] = pack.CompressorType 97 if _, err := writer.Write(buf8); err != nil { 98 return errors.Errorf("write result file error: %s", err.Error()) 99 } 100 101 // ResultSize 102 binary.BigEndian.PutUint32(buf32, pack.ResultSize) 103 if _, err := writer.Write(buf32); err != nil { 104 return errors.Errorf("write result file error: %s", err.Error()) 105 } 106 107 // BodySize 108 binary.BigEndian.PutUint32(buf32, pack.BodySize) 109 if _, err := writer.Write(buf32); err != nil { 110 return errors.Errorf("write result file error: %s", err.Error()) 111 } 112 113 // CertSize 114 binary.BigEndian.PutUint16(buf16, pack.CertSize) 115 if _, err := writer.Write(buf16); err != nil { 116 return errors.Errorf("write result file error: %s", err.Error()) 117 } 118 119 // Certificate 120 if pack.CertSize > 0 { 121 if _, err := writer.Write(pack.Certificate); err != nil { 122 return errors.Errorf("write result file error: %s", err.Error()) 123 } 124 } 125 126 return nil 127 } 128 129 func PersistentJudgeResult( 130 judgeResult *commonStructs.JudgeResult, 131 options *persistence.JudgeResultPersisOptions, 132 ) error { 133 fout, err := os.Create(options.OutFile) 134 if err != nil { 135 return errors.Errorf("create result file error: %s", err.Error()) 136 } 137 defer fout.Close() 138 139 if options.DigitalSign { 140 if options.DigitalPEM.PublicKey == nil || options.DigitalPEM.PrivateKey == nil { 141 return errors.Errorf("digital sign need public key and private key") 142 } 143 } 144 145 resultBytes := utils.ObjectToJSONByte(judgeResult) 146 147 bodyFile, err := mergeResultBinary(options, judgeResult) 148 if err != nil { 149 return err 150 } 151 152 bodyInfo, err := os.Stat(bodyFile) 153 if err != nil { 154 return err 155 } 156 157 certSize := 0 // 0 means disable cert 158 var publicKeyRaw []byte 159 if options.DigitalSign { 160 certSize = len(options.DigitalPEM.PublicKeyRaw) 161 publicKeyRaw = options.DigitalPEM.PublicKeyRaw 162 } 163 164 pack := JudgeResultPackage{ 165 Version: 1, 166 Result: resultBytes, 167 ResultSize: uint32(len(resultBytes)), 168 BodySize: uint32(bodyInfo.Size()), 169 CertSize: uint16(certSize), 170 Certificate: publicKeyRaw, 171 CompressorType: options.CompressorType, 172 } 173 // Write Header 174 err = writeFileHeaderAndResult(fout, pack) 175 if err != nil { 176 return err 177 } 178 179 // Write Signature 180 fBody, err := os.Open(bodyFile) 181 if err != nil { 182 return err 183 } 184 185 hash, err := persistence.SHA256Streams([]io.Reader{ 186 bytes.NewReader(resultBytes), 187 fBody, 188 }) 189 if err != nil { 190 return err 191 } 192 _ = fBody.Close() 193 if options.DigitalSign { 194 hash, err = persistence.RSA2048Sign(hash, options.DigitalPEM.PrivateKey) 195 if err != nil { 196 return err 197 } 198 } 199 buf16 := make([]byte, 2) 200 signSize := uint16(len(hash)) 201 // SignSize 202 binary.BigEndian.PutUint16(buf16, signSize) 203 if _, err := fout.Write(buf16); err != nil { 204 return errors.Errorf("write result file error: %s", err.Error()) 205 } 206 // Signature 207 if _, err := fout.Write(hash); err != nil { 208 return errors.Errorf("write result file error: %s", err.Error()) 209 } 210 211 // Write Result and Body 212 // 要注意先写入result,再写body,方便后续校验的时候直接顺序读取 213 if _, err := fout.Write(pack.Result); err != nil { 214 return errors.Errorf("write result file error: %s", err.Error()) 215 } 216 fBody, err = os.Open(bodyFile) 217 if err != nil { 218 return err 219 } 220 defer fBody.Close() 221 // Copy Body to fout 222 if _, err := io.Copy(fout, fBody); err != nil { 223 return errors.Errorf("write result file error: %s", err.Error()) 224 } 225 // Clean 226 _ = os.Remove(bodyFile) 227 228 return nil 229 }