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  }