Skip to content

Commit 270975d

Browse files
committed
Preserve information whether attachments are executable.
This is relevant for testing tools which are often uploaded as attachments for interactive problems. When we download the samples (via the web interface or API), we should mark these files as executable iff they were executable when uploading.
1 parent 95e6373 commit 270975d

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
final class Version20240917113927 extends AbstractMigration
11+
{
12+
public function getDescription(): string
13+
{
14+
return 'Adding executable bit to problem attachments.';
15+
}
16+
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql('ALTER TABLE problem_attachment_content ADD is_executable TINYINT(1) DEFAULT 0 NOT NULL COMMENT \'Whether this file gets an executable bit.\'');
20+
}
21+
22+
public function down(Schema $schema): void
23+
{
24+
$this->addSql('ALTER TABLE problem_attachment_content DROP is_executable');
25+
}
26+
27+
public function isTransactional(): bool
28+
{
29+
return false;
30+
}
31+
}

webapp/src/Entity/ProblemAttachmentContent.php

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Entity;
44

55
use Doctrine\ORM\Mapping as ORM;
6+
use JMS\Serializer\Annotation as Serializer;
67

78
#[ORM\Entity]
89
#[ORM\Table(options: [
@@ -25,6 +26,10 @@ class ProblemAttachmentContent
2526
#[ORM\Column(type: 'blobtext', options: ['comment' => 'Attachment content'])]
2627
private string $content;
2728

29+
#[ORM\Column(options: ['comment' => 'Whether this file gets an executable bit.', 'default' => 0])]
30+
#[Serializer\Exclude]
31+
private bool $isExecutable = false;
32+
2833
public function getAttachment(): ProblemAttachment
2934
{
3035
return $this->attachment;
@@ -48,4 +53,15 @@ public function setContent(string $content): self
4853

4954
return $this;
5055
}
56+
57+
public function setIsExecutable(bool $isExecutable): ProblemAttachmentContent
58+
{
59+
$this->isExecutable = $isExecutable;
60+
return $this;
61+
}
62+
63+
public function isExecutable(): bool
64+
{
65+
return $this->isExecutable;
66+
}
5167
}

webapp/src/Service/DOMJudgeService.php

+8
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,14 @@ public function getSamplesZipForContest(Contest $contest): StreamedResponse
892892
foreach ($problem->getProblem()->getAttachments() as $attachment) {
893893
$filename = sprintf('%s/attachments/%s', $problem->getShortname(), $attachment->getName());
894894
$zip->addFromString($filename, $attachment->getContent()->getContent());
895+
if ($attachment->getContent()->isExecutable()) {
896+
// 100755 = regular file, executable
897+
$zip->setExternalAttributesName(
898+
$filename,
899+
ZipArchive::OPSYS_UNIX,
900+
octdec('100755') << 16
901+
);
902+
}
895903
}
896904
}
897905

webapp/src/Service/ImportProblemService.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,14 @@ public function importZippedProblem(
539539
continue;
540540
}
541541

542+
// In doubt make files executable, but try to read it from the zip file.
543+
$executableBit = true;
544+
if ($zip->getExternalAttributesIndex($j, $opsys, $attr)
545+
&& $opsys==ZipArchive::OPSYS_UNIX
546+
&& (($attr >> 16) & 0100) === 0) {
547+
$executableBit = false;
548+
}
549+
542550
$name = basename($filename);
543551

544552
$fileParts = explode('.', $name);
@@ -558,6 +566,10 @@ public function importZippedProblem(
558566
$messages['info'][] = sprintf("Updated attachment '%s'", $name);
559567
$numAttachments++;
560568
}
569+
if ($executableBit !== $attachmentContent->isExecutable()) {
570+
$attachmentContent->setIsExecutable($executableBit);
571+
$messages['info'][] = sprintf("Updated executable bit of attachment '%s'", $name);
572+
}
561573
} else {
562574
$attachment = new ProblemAttachment();
563575
$attachmentContent = new ProblemAttachmentContent();
@@ -567,7 +579,9 @@ public function importZippedProblem(
567579
->setType($type)
568580
->setContent($attachmentContent);
569581

570-
$attachmentContent->setContent($content);
582+
$attachmentContent
583+
->setContent($content)
584+
->setIsExecutable($executableBit);
571585

572586
$this->em->persist($attachment);
573587

0 commit comments

Comments
 (0)