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  }