judge: 限制测试包中文件数量

This commit is contained in:
undefined committed 2020-10-30 21:27:53 +08:00
1 parent de7223e055
commit 1231fb2ce7
5 files changed
+32 -19

No files matched your search

+2 -1
View File
@@ -1,11 +1,12 @@
{ {
"name": "@hydrooj/hydrojudge", "name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js", "bin": "bin/hydrojudge.js",
"version": "2.1.13", "version": "2.1.14",
"main": "package.json", "main": "package.json",
"author": "masnn", "author": "masnn",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
"dependencies": { "dependencies": {
"adm-zip": "^0.4.16",
"axios": "^0.21.0", "axios": "^0.21.0",
"bson": "^4.1.0", "bson": "^4.1.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
+7 -4
View File
@@ -243,7 +243,7 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
} else if (cfg.subtasks) { } else if (cfg.subtasks) {
for (const subtask of cfg.subtasks) { for (const subtask of cfg.subtasks) {
const cases = []; const cases = [];
for (const c of subtask) { for (const c of subtask.cases) {
config.count++; config.count++;
cases.push({ cases.push({
input: checkFile(c.input, '找不到输入文件 '), input: checkFile(c.input, '找不到输入文件 '),
@@ -269,14 +269,17 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
function convertIniConfig(ini: string) { function convertIniConfig(ini: string) {
const f = ini.split('\n'); const f = ini.split('\n');
const count = parseInt(f[0], 10); const count = parseInt(f[0], 10);
const res = { cases: [] }; const res = { subtasks: [] };
for (let i = 1; i <= count; i++) { for (let i = 1; i <= count; i++) {
const [input, output, time, score, memory] = f[i].split('|'); const [input, output, time, score, memory] = f[i].split('|');
const cur = { const cur = {
input: `input/${input}`, output: `output/${output}`, score: parseInt(score, 10), time: `${time}s`, memory: '128m', cases: [{ input: `input/${input}`, output: `output/${output}` }],
score: parseInt(score, 10),
time: `${time}s`,
memory: '128m',
}; };
if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`; if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`;
res.cases.push(cur); res.subtasks.push(cur);
} }
return res; return res;
} }
+14 -8
View File
@@ -1,12 +1,12 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import path from 'path'; import path from 'path';
import child from 'child_process';
import axios from 'axios'; import axios from 'axios';
import AdmZip from 'adm-zip';
import fs from 'fs-extra'; import fs from 'fs-extra';
import WebSocket from 'ws'; import WebSocket from 'ws';
import * as tmpfs from '../tmpfs'; import * as tmpfs from '../tmpfs';
import log from '../log'; import log from '../log';
import { compilerText } from '../utils'; import { compilerText, Queue } from '../utils';
import { CACHE_DIR, TEMP_DIR } from '../config'; import { CACHE_DIR, TEMP_DIR } from '../config';
import { FormatError, CompileError, SystemError } from '../error'; import { FormatError, CompileError, SystemError } from '../error';
import { STATUS_COMPILE_ERROR, STATUS_SYSTEM_ERROR } from '../status'; import { STATUS_COMPILE_ERROR, STATUS_SYSTEM_ERROR } from '../status';
@@ -240,15 +240,18 @@ export default class VJ4 {
`${this.config.server_url}d/${domainId}/p/${pid}/data`, `${this.config.server_url}d/${domainId}/p/${pid}/data`,
{ responseType: 'stream' }, { responseType: 'stream' },
); );
const w = await fs.createWriteStream(tmpFilePath); const w = fs.createWriteStream(tmpFilePath);
res.data.pipe(w); res.data.pipe(w);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
w.on('finish', resolve); w.on('finish', resolve);
w.on('error', reject); w.on('error', reject);
}); });
fs.ensureDirSync(path.dirname(savePath)); fs.ensureDirSync(path.dirname(savePath));
const zip = new AdmZip(tmpFilePath);
const entries = zip.getEntries();
if (entries.length > 1000) throw new FormatError('Too many files');
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
child.exec(`unzip ${tmpFilePath} -d ${savePath}`, (e) => { zip.extractAllToAsync(savePath, true, (e) => {
if (e) reject(e); if (e) reject(e);
else resolve(); else resolve();
}); });
@@ -262,20 +265,23 @@ export default class VJ4 {
return savePath; return savePath;
} }
async recordPretestData(rid, savePath) { async recordPretestData(rid: string, savePath: string) {
log.info(`Getting pretest data: ${this.config.host}/${rid}`); log.info(`Getting pretest data: ${this.config.host}/${rid}`);
const tmpFilePath = path.resolve(CACHE_DIR, `download_${this.config.host}_${rid}`); const tmpFilePath = path.resolve(CACHE_DIR, `download_${this.config.host}_${rid}`);
await this.ensureLogin(); await this.ensureLogin();
const res = await this.axios.get(`records/${rid}/data`, { responseType: 'stream' }); const res = await this.axios.get(`records/${rid}/data`, { responseType: 'stream' });
const w = await fs.createWriteStream(tmpFilePath); const w = fs.createWriteStream(tmpFilePath);
res.data.pipe(w); res.data.pipe(w);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
w.on('finish', resolve); w.on('finish', resolve);
w.on('error', reject); w.on('error', reject);
}); });
fs.ensureDirSync(path.dirname(savePath)); fs.ensureDirSync(path.dirname(savePath));
const zip = new AdmZip(tmpFilePath);
const entries = zip.getEntries();
if (entries.length > 1000) throw new FormatError('Too many files');
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
child.exec(`unzip ${tmpFilePath} -d ${savePath}`, (e) => { zip.extractAllToAsync(savePath, true, (e) => {
if (e) reject(e); if (e) reject(e);
else resolve(); else resolve();
}); });
@@ -285,7 +291,7 @@ export default class VJ4 {
return savePath; return savePath;
} }
async consume(queue) { async consume(queue: Queue<any>) {
log.info('正在连接 %sjudge/consume-conn', this.config.server_url); log.info('正在连接 %sjudge/consume-conn', this.config.server_url);
const res = await this.axios.get('judge/consume-conn/info'); const res = await this.axios.get('judge/consume-conn/info');
this.ws = new WebSocket(`${this.config.server_url.replace(/^http/i, 'ws')}judge/consume-conn/websocket?t=${res.data.entropy}`, { this.ws = new WebSocket(`${this.config.server_url.replace(/^http/i, 'ws')}judge/consume-conn/websocket?t=${res.data.entropy}`, {
+5 -2
View File
@@ -2,10 +2,10 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import 'hydrooj'; import 'hydrooj';
import path from 'path'; import path from 'path';
import child from 'child_process';
import { ObjectID } from 'bson'; import { ObjectID } from 'bson';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { homedir, tmpdir } from 'os'; import { homedir, tmpdir } from 'os';
import AdmZip from 'adm-zip';
declare module 'hydrooj/dist/interface' { declare module 'hydrooj/dist/interface' {
interface SystemKeys { interface SystemKeys {
@@ -73,8 +73,11 @@ async function postInit() {
w.on('error', reject); w.on('error', reject);
}); });
fs.ensureDirSync(path.dirname(savePath)); fs.ensureDirSync(path.dirname(savePath));
const zip = new AdmZip(tmpFilePath);
const entries = zip.getEntries();
if (entries.length > 1000) throw new FormatError('Too many files');
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
child.exec(`unzip ${tmpFilePath} -d ${savePath}`, (e) => { zip.extractAllToAsync(savePath, true, (e) => {
if (e) reject(e); if (e) reject(e);
else resolve(); else resolve();
}); });
+4 -4
View File
@@ -13,19 +13,19 @@ const EMPTY_STR = /^[ \r\n\t]*$/i;
export const cmd = parse; export const cmd = parse;
export function parseTimeMS(str) { export function parseTimeMS(str: string) {
const match = TIME_RE.exec(str); const match = TIME_RE.exec(str);
if (!match) throw new FormatError(str, ['error parsing time']); if (!match) throw new FormatError(str, ['error parsing time']);
return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]); return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]);
} }
export function parseMemoryMB(str) { export function parseMemoryMB(str: string) {
const match = MEMORY_RE.exec(str); const match = MEMORY_RE.exec(str);
if (!match) throw new FormatError(str, ['error parsing memory']); if (!match) throw new FormatError(str, ['error parsing memory']);
return Math.floor(parseFloat(match[1]) * MEMORY_UNITS[match[2]]); return Math.floor(parseFloat(match[1]) * MEMORY_UNITS[match[2]]);
} }
export function sleep(timeout) { export function sleep(timeout: number) {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve(); resolve();
@@ -84,7 +84,7 @@ export function compilerText(stdout: string, stderr: string) {
return ret.join('\n'); return ret.join('\n');
} }
export function copyInDir(dir) { export function copyInDir(dir: string) {
const files = {}; const files = {};
if (fs.existsSync(dir)) { if (fs.existsSync(dir)) {
fs.readdirSync(dir).forEach((f1) => { fs.readdirSync(dir).forEach((f1) => {