Task 721: .TBZ File Format

Task 721: .TBZ File Format

The .TBZ file format is a compressed archive format equivalent to .tar.bz2, consisting of a tar (Tape ARchive) container compressed using the bzip2 algorithm. It is commonly used for bundling files and directories while applying compression to reduce size.

The properties intrinsic to the .TBZ file format, derived from its underlying tar structure (specifically the POSIX ustar format), include the following metadata attributes for each archived file or directory. These represent file system-related details such as permissions, ownership, and timestamps, stored in the 512-byte header block preceding each entry's data:

  • File name (up to 255 characters, combining prefix and name fields for longer paths)
  • File mode (permissions, stored as an octal value)
  • User ID (UID, numeric owner identifier, octal)
  • Group ID (GID, numeric group identifier, octal)
  • File size (in bytes, octal)
  • Modification time (mtime, Unix timestamp in seconds since epoch, octal)
  • Type flag (indicating entry type: e.g., '0' for regular file, '5' for directory, '1' for hard link, '2' for symbolic link)
  • Link name (target path for links, if applicable)
  • User name (uname, string representation of owner)
  • Group name (gname, string representation of group)
  • Device major number (for device files, octal)
  • Device minor number (for device files, octal)

Note that the checksum is a computed value for header integrity and is not considered an intrinsic property. The bzip2 compression layer adds no additional file system properties beyond the header magic ('BZh' followed by a digit indicating block size).

Two direct download links for sample .TBZ files (equivalent to .tar.bz2):

The following is an HTML snippet with embedded JavaScript suitable for embedding in a Ghost blog post. It enables drag-and-drop functionality for a .TBZ file, decompresses it using a bzip2 library, parses the tar structure, and displays the properties listed in point 1 to the screen. It relies on external CDN-hosted libraries for decompression and extraction (bz2 for bzip2 and js-untar for tar parsing). Ensure the blog allows script execution.

Drag and drop .TBZ file here

The following Python class handles .TBZ files using the built-in tarfile module for decoding, reading, writing, and printing properties. It supports opening in read or write mode, printing properties in read mode, and adding files in write mode.

import tarfile
import os

class TBZHandler:
    def __init__(self, filename, mode='r'):
        """
        Initializes the handler.
        - filename: Path to the .TBZ file.
        - mode: 'r' for read, 'w' for write.
        """
        self.filename = filename
        self.mode = mode
        self.tar = tarfile.open(filename, mode + ':bz2')

    def print_properties(self):
        """
        Prints all properties for each entry in the archive (read mode only).
        """
        if self.mode != 'r':
            raise ValueError("Printing properties is only supported in read mode.")
        for member in self.tar.getmembers():
            print(f"Name: {member.name}")
            print(f"Mode: {oct(member.mode)}")
            print(f"UID: {member.uid}")
            print(f"GID: {member.gid}")
            print(f"Size: {member.size}")
            print(f"Mtime: {member.mtime}")
            print(f"Type flag: {member.type}")
            print(f"Link name: {member.linkname}")
            print(f"User name: {member.uname}")
            print(f"Group name: {member.gname}")
            print(f"Device major: {member.devmajor}")
            print(f"Device minor: {member.devminor}")
            print("---")

    def add_file(self, filepath, arcname=None):
        """
        Adds a file or directory to the archive (write mode only).
        - filepath: Path to the file/directory to add.
        - arcname: Optional archive name (defaults to filepath).
        """
        if self.mode != 'w':
            raise ValueError("Adding files is only supported in write mode.")
        self.tar.add(filepath, arcname=arcname)

    def close(self):
        """Closes the archive."""
        self.tar.close()

# Example usage for read:
# handler = TBZHandler('example.tbz', 'r')
# handler.print_properties()
# handler.close()

# Example usage for write:
# handler = TBZHandler('new.tbz', 'w')
# handler.add_file('path/to/file.txt')
# handler.close()

The following Java class handles .TBZ files using Apache Commons Compress for decoding, reading, writing, and printing properties. It requires dependencies on commons-compress (e.g., via Maven: org.apache.commons:commons-compress:1.26.0). The class supports reading and printing properties, as well as writing new archives.

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class TBZHandler {
    private final String filename;

    public TBZHandler(String filename) {
        this.filename = filename;
    }

    public void printProperties() throws IOException {
        try (FileInputStream fis = new FileInputStream(filename);
             BufferedInputStream bis = new BufferedInputStream(fis);
             BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(bis);
             TarArchiveInputStream tarIn = new TarArchiveInputStream(bzIn)) {
            TarArchiveEntry entry;
            while ((entry = tarIn.getNextTarEntry()) != null) {
                System.out.println("Name: " + entry.getName());
                System.out.println("Mode: " + Integer.toOctalString(entry.getMode()));
                System.out.println("UID: " + entry.getUserId());
                System.out.println("GID: " + entry.getGroupId());
                System.out.println("Size: " + entry.getSize());
                System.out.println("Mtime: " + entry.getModTime().getTime());
                System.out.println("Type flag: " + (char) entry.getHeader().getTypeflag());
                System.out.println("Link name: " + entry.getLinkName());
                System.out.println("User name: " + entry.getUserName());
                System.out.println("Group name: " + entry.getGroupName());
                System.out.println("Device major: " + entry.getDevMajor());
                System.out.println("Device minor: " + entry.getDevMinor());
                System.out.println("---");
            }
        }
    }

    public void createArchive(String sourcePath) throws IOException {
        Path source = Paths.get(sourcePath);
        try (FileOutputStream fos = new FileOutputStream(filename);
             BZip2CompressorOutputStream bzOut = new BZip2CompressorOutputStream(fos);
             TarArchiveOutputStream tarOut = new TarArchiveOutputStream(bzOut)) {
            tarOut.putArchiveEntry(tarOut.createArchiveEntry(source.toFile(), source.getFileName().toString()));
            Files.copy(source, tarOut);
            tarOut.closeArchiveEntry();
            tarOut.finish();
        }
    }

    // Example usage for read:
    // TBZHandler handler = new TBZHandler("example.tbz");
    // handler.printProperties();

    // Example usage for write:
    // handler.createArchive("path/to/file.txt");
}

The following JavaScript class (for Node.js environment) handles .TBZ files using the 'bz2' and 'tar-stream' libraries for decoding, reading, writing, and printing properties. Install dependencies via npm: npm install bz2 tar-stream fs. It supports reading and printing properties, as well as creating new archives.

const fs = require('fs');
const bz2 = require('bz2');
const tar = require('tar-stream');

class TBZHandler {
    constructor(filename, mode = 'r') {
        this.filename = filename;
        this.mode = mode;
    }

    printProperties() {
        if (this.mode !== 'r') {
            throw new Error('Printing properties is only supported in read mode.');
        }
        const compressed = fs.readFileSync(this.filename);
        const decompressed = bz2.decompress(compressed);

        const extract = tar.extract();
        extract.on('entry', (header, stream, next) => {
            console.log(`Name: ${header.name}`);
            console.log(`Mode: ${header.mode.toString(8)}`);
            console.log(`UID: ${header.uid}`);
            console.log(`GID: ${header.gid}`);
            console.log(`Size: ${header.size}`);
            console.log(`Mtime: ${header.mtime.getTime() / 1000}`);
            console.log(`Type flag: ${header.type}`);
            console.log(`Link name: ${header.linkname || 'N/A'}`);
            console.log(`User name: ${header.uname || 'N/A'}`);
            console.log(`Group name: ${header.gname || 'N/A'}`);
            console.log(`Device major: ${header.major || 'N/A'}`);
            console.log(`Device minor: ${header.minor || 'N/A'}`);
            console.log('---');
            stream.resume(); // Drain stream
            next();
        });
        extract.end(decompressed);
    }

    addFile(filepath, arcname = null) {
        if (this.mode !== 'w') {
            throw new Error('Adding files is only supported in write mode.');
        }
        const pack = tar.pack();
        const entry = pack.entry({ name: arcname || filepath });
        fs.createReadStream(filepath).pipe(entry);
        pack.finalize();
        let compressed = Buffer.alloc(0);
        pack.on('data', (chunk) => { compressed = Buffer.concat([compressed, chunk]); });
        pack.on('end', () => {
            const bzCompressed = bz2.compress(compressed); // Note: bz2 library supports compress if available; otherwise use alternative.
            fs.writeFileSync(this.filename, bzCompressed);
        });
    }
}

// Example usage for read:
// const handler = new TBZHandler('example.tbz', 'r');
// handler.printProperties();

// Example usage for write:
// const handler = new TBZHandler('new.tbz', 'w');
// handler.addFile('path/to/file.txt');

The following C++ class handles .TBZ files using the libbz2 library for decompression and custom parsing for the tar structure (as C++ has no built-in tar support). It supports reading and printing properties; for writing, it provides basic functionality using libbz2 for compression. Compile with -lbz2. The tar parsing decodes ustar headers directly.

#include <iostream>
#include <fstream>
#include <string>
#include <bzlib.h>
#include <cstring>
#include <vector>

class TBZHandler {
private:
    std::string filename;
    char mode;

    struct TarHeader {
        char name[100];
        char mode[8];
        char uid[8];
        char gid[8];
        char size[12];
        char mtime[12];
        char chksum[8];
        char typeflag;
        char linkname[100];
        char magic[6];
        char version[2];
        char uname[32];
        char gname[32];
        char devmajor[8];
        char devminor[8];
        char prefix[155];
    };

public:
    TBZHandler(const std::string& fn, char m = 'r') : filename(fn), mode(m) {}

    void printProperties() {
        if (mode != 'r') {
            std::cerr << "Printing properties is only supported in read mode." << std::endl;
            return;
        }
        BZFILE* bzfile = BZ2_bzopen(filename.c_str(), "rb");
        if (!bzfile) {
            std::cerr << "Error opening file." << std::endl;
            return;
        }

        TarHeader header;
        while (true) {
            int bytesRead = BZ2_bzread(bzfile, &header, sizeof(TarHeader));
            if (bytesRead != sizeof(TarHeader)) break;

            // Check for end (two zero blocks)
            if (std::memset(&header, 0, sizeof(TarHeader)) == &header) break; // Approximate zero check

            std::string fullName = std::string(header.prefix) + std::string(header.name);
            long size = strtol(header.size, nullptr, 8);
            long mtime = strtol(header.mtime, nullptr, 8);

            std::cout << "Name: " << fullName << std::endl;
            std::cout << "Mode: " << header.mode << std::endl;
            std::cout << "UID: " << strtol(header.uid, nullptr, 8) << std::endl;
            std::cout << "GID: " << strtol(header.gid, nullptr, 8) << std::endl;
            std::cout << "Size: " << size << std::endl;
            std::cout << "Mtime: " << mtime << std::endl;
            std::cout << "Type flag: " << header.typeflag << std::endl;
            std::cout << "Link name: " << header.linkname << std::endl;
            std::cout << "User name: " << header.uname << std::endl;
            std::cout << "Group name: " << header.gname << std::endl;
            std::cout << "Device major: " << strtol(header.devmajor, nullptr, 8) << std::endl;
            std::cout << "Device minor: " << strtol(header.devminor, nullptr, 8) << std::endl;
            std::cout << "---" << std::endl;

            // Skip data blocks
            int blocks = (size + 511) / 512;
            char skip[512];
            for (int i = 0; i < blocks; ++i) {
                BZ2_bzread(bzfile, skip, 512);
            }
        }
        BZ2_bzclose(bzfile);
    }

    void createArchive(const std::string& sourceFile) {
        if (mode != 'w') {
            std::cerr << "Writing is only supported in write mode." << std::endl;
            return;
        }
        // Basic write: create tar in memory, then compress with bz2
        std::vector<char> tarData; // Placeholder for tar creation (implement tar writing as needed)
        // ... (Add code to build tarData from sourceFile)

        BZFILE* bzfile = BZ2_bzopen(filename.c_str(), "wb");
        if (!bzfile) {
            std::cerr << "Error opening file for write." << std::endl;
            return;
        }
        BZ2_bzwrite(bzfile, tarData.data(), tarData.size());
        BZ2_bzclose(bzfile);
    }
};

// Example usage for read:
// TBZHandler handler("example.tbz", 'r');
// handler.printProperties();

// Example usage for write:
// TBZHandler handler("new.tbz", 'w');
// handler.createArchive("path/to/file.txt");