← All tasks
javacodex/java-t1 #38Not a task: already works

Tar Archive Handler (java, written by Codex)

envgap__codex__java-t1-38

Written by a coding agent; not on GitHubWritten 2026-03-03

01 / FAILURE SIGNATURE

As the study recorded it

None
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/java-t1 #38 · read the task the agent was given
Codex wrote this java project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Tar Archive Handler

Write a program that creates, extracts, lists, and manipulates TAR archives with support for multiple compression methods (gzip, bzip2, xz), preserving file permissions and ownership metadata.

FUNCTIONAL REQUIREMENTS:
- Support subcommands: create, extract, list, and append
- create: Bundle files and directories into a TAR archive, recursively including directory contents with preserved file permissions, ownership, and symbolic links
- extract: Unpack a TAR archive to a specified directory via --output flag, preserving directory structure and file permissions
- list: Display archive contents showing: file name, size, permissions, owner, modification date, and file type (regular, directory, symlink)
- Support compression methods selectable via --compress flag: none (plain .tar), gzip (.tar.gz), bzip2 (.tar.bz2), xz (.tar.xz); auto-detect during extraction
- append: Add files to an existing uncompressed TAR archive
- Support file filtering via --include and --exclude flags with glob patterns
- Support extracting specific files from an archive by listing their paths as additional arguments
- Verify archive integrity via --verify flag
- Print operation summary to console: total files, total size, compression ratio (if compressed), and processing time
- Save operation log as JSON with --log flag
- If no arguments are given, create a sample directory structure with files of various types, create archives in each compression format, list contents of each, extract one, and compare compression ratios
- Handle errors: corrupted archives, unsupported formats, path traversal attacks, and permission issues during extraction

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.tmlr</groupId>
  <artifactId>tar-archive-handler</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.7.1</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>TarArchiveHandler</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
README.md
# Tar Archive Handler (Java)

## Requirements
- Ubuntu 22.04
- JDK 17+
- Maven 3.8+
- System `tar` utility with gzip/bzip2/xz support

## Build
```bash
mvn -q -DskipTests package assembly:single
```

## Run
```bash
java -cp target/tar-archive-handler-1.0.0-jar-with-dependencies.jar TarArchiveHandler create archive_base input_dir --compress gzip
java -cp target/tar-archive-handler-1.0.0-jar-with-dependencies.jar TarArchiveHandler list archive_base.tar.gz
java -cp target/tar-archive-handler-1.0.0-jar-with-dependencies.jar TarArchiveHandler extract archive_base.tar.gz --output extracted
java -cp target/tar-archive-handler-1.0.0-jar-with-dependencies.jar TarArchiveHandler append archive_base.tar file1 file2
```

If no arguments are passed, sample archives are generated and analyzed.
src/main/java/TarArchiveHandler.java
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.Instant;
import java.util.*;

public class TarArchiveHandler {
    private static class Config {
        String cmd;
        String compress = "none";
        List<String> include = new ArrayList<>();
        List<String> exclude = new ArrayList<>();
        String output;
        boolean verify;
        String log;
        List<String> positional = new ArrayList<>();
    }

    public static void main(String[] args) {
        try {
            Config cfg = parseArgs(args);
            if (cfg.cmd == null) {
                runDemo();
                return;
            }

            long start = System.nanoTime();
            Map<String, Object> result = switch (cfg.cmd) {
                case "create" -> createArchive(cfg);
                case "extract" -> extractArchive(cfg);
                case "list" -> listArchive(cfg);
                case "append" -> appendArchive(cfg);
                default -> throw new IllegalArgumentException("Unknown command: " + cfg.cmd);
            };

            @SuppressWarnings("unchecked")
            List<String> entries = (List<String>) result.get("entries");
            if (entries != null) entries.forEach(System.out::println);
            if (result.containsKey("summary")) {
                System.out.println("Summary: " + result.get("summary"));
            }
            double ms = (System.nanoTime() - start) / 1_000_000.0;
            System.out.printf("Time: %.2f ms%n", ms);

            String logPath = cfg.log != null ? cfg.log : ("list".equals(cfg.cmd) ? null : "tar_operation_log.json");
            if (logPath != null) Files.writeString(Paths.get(logPath), toJson(result), StandardCharsets.UTF_8);
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(1);
        }
    }

    private static Config parseArgs(String[] args) {
        Config cfg = new Config();
        int i = 0;
        if (args.length > 0 && !args[0].startsWith("--")) {
            cfg.cmd = args[0];
            i = 1;
        }
        for (; i < args.length; i++) {
            String a = args[i];
            if (!a.startsWith("--")) {
                cfg.positional.add(a);
                continue;
            }
            switch (a) {
                case "--compress" -> cfg.compress = args[++i];
                case "--include" -> cfg.include.add(args[++i]);
                case "--exclude" -> cfg.exclude.add(args[++i]);
                case "--output" -> cfg.output = args[++i];
                case "--verify" -> cfg.verify = true;
                case "--log" -> cfg.log = args[++i];
                default -> throw new IllegalArgumentException("Unknown option: " + a);
            }
        }
        return cfg;
    }

    private static String q(String s) { return '"' + s.replace("\"", "\\\"") + '"'; }

    private static String run(String cmd) throws Exception {
        Process p = new ProcessBuilder("bash", "-lc", cmd).redirectErrorStream(true).start();
        String out;
        try (InputStream in = p.getInputStream()) { out = new String(in.readAllBytes(), StandardCharsets.UTF_8); }
        int rc = p.waitFor();
        if (rc != 0) throw new RuntimeException("Command failed (" + rc + "): " + cmd + "\n" + out);
        return out;
    }

    private static String flags(String compress) {
        return switch (compress) {
            case "gzip" -> "z";
            case "bzip2" -> "j";
            case "xz" -> "J";
            default -> "";
        };
    }

    private static String defaultArchive(String base, String compress) {
        return switch (compress) {
            case "gzip" -> base + ".tar.gz";
            case "bzip2" -> base + ".tar.bz2";
            case "xz" -> base + ".tar.xz";
            default -> base + ".tar";
        };
    }

    private static List<String> listEntries(String archive) throws Exception {
        String out = run("tar -tvf " + q(archive));
        return out.lines().filter(s -> !s.isBlank()).toList();
    }

    private static Map<String, Object> createArchive(Config cfg) throws Exception {
        if (cfg.positional.size() < 2) throw new IllegalArgumentException("Usage: create <archive_base_or_path> <file|dir> [...]");
        String archive = cfg.output != null ? cfg.output : defaultArchive(cfg.positional.get(0), cfg.compress);
        List<String> inputs = cfg.positional.subList(1, cfg.positional.size());

        StringBuilder cmd = new StringBuilder("tar -c" + flags(cfg.compress) + "f " + q(archive) + " ");
        for (String ex : cfg.exclude) cmd.append("--exclude=").append(q(ex)).append(' ');
        for (String in : inputs) cmd.append(q(in)).append(' ');
        run(cmd.toString());

        List<String> entries = listEntries(archive);
        Map<String, Object> res = new LinkedHashMap<>();
        res.put("operation", "create");
        res.put("archive", Paths.get(archive).toAbsolutePath().toString());
        res.put("entries", entries);
        res.put("summary", Map.of("totalFiles", entries.size()));
        return res;
    }

    private static Map<String, Object> extractArchive(Config cfg) throws Exception {
        if (cfg.positional.isEmpty()) throw new IllegalArgumentException("Usage: extract <archive> [paths...]");
        String archive = cfg.positional.get(0);
        String outDir = cfg.output == null ? "." : cfg.output;
        Files.createDirectories(Paths.get(outDir));

        String files = String.join(" ", cfg.positional.subList(1, cfg.positional.size()).stream().map(TarArchiveHandler::q).toList());
        run("tar -xf " + q(archive) + " -C " + q(outDir) + " " + files);

        Boolean verify = null;
        if (cfg.verify) {
            run("tar -tf " + q(archive));
            verify = true;
        }

        return new LinkedHashMap<>(Map.of(
            "operation", "extract",
            "archive", Paths.get(archive).toAbsolutePath().toString(),
            "outputDirectory", Paths.get(outDir).toAbsolutePath().toString(),
            "verify", verify
        ));
    }

    private static Map<String, Object> listArchive(Config cfg) throws Exception {
        if (cfg.positional.isEmpty()) throw new IllegalArgumentException("Usage: list <archive>");
        String archive = cfg.positional.get(0);
        List<String> entries = listEntries(archive);
        Map<String, Object> res = new LinkedHashMap<>();
        res.put("operation", "list");
        res.put("archive", Paths.get(archive).toAbsolutePath().toString());
        res.put("entries", entries);
        res.put("summary", Map.of("totalFiles", entries.size()));
        return res;
    }

    private static Map<String, Object> appendArchive(Config cfg) throws Exception {
        if (cfg.positional.size() < 2) throw new IllegalArgumentException("Usage: append <archive.tar> <file|dir> [...]");
        String archive = cfg.positional.get(0);
        if (!archive.endsWith(".tar")) throw new IllegalArgumentException("append only supports uncompressed .tar archives");
        String inputs = String.join(" ", cfg.positional.subList(1, cfg.positional.size()).stream().map(TarArchiveHandler::q).toList());
        run("tar -rf " + q(archive) + " " + inputs);

        List<String> entries = listEntries(archive);
        Map<String, Object> res = new LinkedHashMap<>();
        res.put("operation", "append");
        res.put("archive", Paths.get(archive).toAbsolutePath().toString());
        res.put("entries", entries);
        res.put("summary", Map.of("totalFiles", entries.size()));
        return res;
    }

    private static void runDemo() throws Exception {
        Path root = Paths.get("sample_tar_data").toAbsolutePath();
        deleteRec(root);
        Files.createDirectories(root.resolve("docs"));
        Files.createDirectories(root.resolve("assets"));
        Files.writeString(root.resolve("docs/readme.txt"), "Sample tar data\n", StandardCharsets.UTF_8);
        Files.writeString(root.resolve("docs/table.csv"), "id,name\n1,A\n2,B\n", StandardCharsets.UTF_8);
        Files.write(root.resolve("assets/blob.bin"), new byte[2048]);

        Map<String, Object> plain = createArchive(newConfig("create", List.of("sample_plain", root.toString()), "none", null));
        Map<String, Object> gzip = createArchive(newConfig("create", List.of("sample_gzip", root.toString()), "gzip", null));
        Map<String, Object> bzip2 = createArchive(newConfig("create", List.of("sample_bzip2", root.toString()), "bzip2", null));
        Map<String, Object> xz = createArchive(newConfig("create", List.of("sample_xz", root.toString()), "xz", null));
        Map<String, Object> list = listArchive(newConfig("list", List.of((String) gzip.get("archive")), "none", null));
        Config ex = newConfig("extract", List.of((String) gzip.get("archive")), "none", "sample_tar_extract");
        ex.verify = true;
        Map<String, Object> extract = extractArchive(ex);

        Map<String, Object> report = new LinkedHashMap<>();
        report.put("generatedAt", Instant.now().toString());
        report.put("plain", plain);
        report.put("gzip", gzip);
        report.put("bzip2", bzip2);
        report.put("xz", xz);
        report.put("list", list);
        report.put("extract", extract);

        Files.writeString(Paths.get("tar_demo_report.json"), toJson(report), StandardCharsets.UTF_8);
        @SuppressWarnings("unchecked") List<String> entries = (List<String>) list.get("entries");
        entries.forEach(System.out::println);
    }

    private static Config newConfig(String cmd, List<String> positional, String compress, String output) {
        Config c = new Config();
        c.cmd = cmd;
        c.positional = new ArrayList<>(positional);
        c.compress = compress;
        c.output = output;
        return c;
    }

    private static void deleteRec(Path p) throws IOException {
        if (!Files.exists(p)) return;
        if (Files.isDirectory(p)) {
            try (DirectoryStream<Path> ds = Files.newDirectoryStream(p)) {
                for (Path c : ds) deleteRec(c);
            }
        }
        Files.deleteIfExists(p);
    }

    private static String toJson(Object obj) {
        if (obj == null) return "null";
        if (obj instanceof String s) return '"' + s.replace("\\", "\\\\").replace("\"", "\\\"") + '"';
        if (obj instanceof Number || obj instanceof Boolean) return obj.toString();
        if (obj instanceof Map<?, ?> m) {
            StringBuilder sb = new StringBuilder("{");
            boolean first = true;
            for (Map.Entry<?, ?> e : m.entrySet()) {
                if (!first) sb.append(',');
                first = false;
                sb.append(toJson(String.valueOf(e.getKey()))).append(':').append(toJson(e.getValue()));
            }
            sb.append('}');
            return sb.toString();
        }
        if (obj instanceof Iterable<?> it) {
            StringBuilder sb = new StringBuilder("[");
            boolean first = true;
            for (Object x : it) {
                if (!first) sb.append(',');
                first = false;
                sb.append(toJson(x));
            }
            sb.append(']');
            return sb.toString();
        }
        return toJson(String.valueOf(obj));
    }
}