Skip to content

Commit 3216d0d

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 f30e6d3 commit 3216d0d

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed
Lines changed: 31 additions & 0 deletions
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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class ProblemAttachmentContent
2525
#[ORM\Column(type: 'blobtext', options: ['comment' => 'Attachment content'])]
2626
private string $content;
2727

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

4953
return $this;
5054
}
55+
56+
public function setIsExecutable(bool $isExecutable): ProblemAttachmentContent
57+
{
58+
$this->isExecutable = $isExecutable;
59+
return $this;
60+
}
61+
62+
public function isExecutable(): bool
63+
{
64+
return $this->isExecutable;
65+
}
5166
}

webapp/src/Service/DOMJudgeService.php

Lines changed: 8 additions & 0 deletions
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

Lines changed: 15 additions & 1 deletion
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)