xref: /plugin/sitebackup/PatchedTar.php (revision a33f8e80b33300b26ee79ef1915dbb5b5dc26900)
1*a33f8e80Stracker-user<?php
2*a33f8e80Stracker-user
3*a33f8e80Stracker-usernamespace dokuwiki\plugin\sitebackup;
4*a33f8e80Stracker-user
5*a33f8e80Stracker-useruse splitbrain\PHPArchive\Tar as UpstreamTar;
6*a33f8e80Stracker-user
7*a33f8e80Stracker-user/**
8*a33f8e80Stracker-user * Tar with the upstream mtime bug patched.
9*a33f8e80Stracker-user *
10*a33f8e80Stracker-user * DokuWiki 2025-05-14b "Librarian" ships an old version of
11*a33f8e80Stracker-user * splitbrain/php-archive whose Tar::writeRawFileHeader() contains:
12*a33f8e80Stracker-user *     $size  = self::numberEncode($size, 12);
13*a33f8e80Stracker-user *     $mtime = self::numberEncode($size, 12);   // <-- $size, not $mtime
14*a33f8e80Stracker-user * So every file's mtime field ends up holding its size, octal-encoded. GNU tar
15*a33f8e80Stracker-user * dutifully reads the value as a Unix timestamp and shows 1970-01-01 with a
16*a33f8e80Stracker-user * size-derived seconds offset.
17*a33f8e80Stracker-user *
18*a33f8e80Stracker-user * Fixed upstream in splitbrain/php-archive PR #38. This subclass copies the
19*a33f8e80Stracker-user * method verbatim and corrects the one line so existing DokuWiki installs
20*a33f8e80Stracker-user * don't have to wait for the vendored library to be bumped.
21*a33f8e80Stracker-user */
22*a33f8e80Stracker-userclass PatchedTar extends UpstreamTar
23*a33f8e80Stracker-user{
24*a33f8e80Stracker-user    /**
25*a33f8e80Stracker-user     * @inheritdoc
26*a33f8e80Stracker-user     */
27*a33f8e80Stracker-user    protected function writeRawFileHeader($name, $uid, $gid, $perm, $size, $mtime, $typeflag = '')
28*a33f8e80Stracker-user    {
29*a33f8e80Stracker-user        // handle filename length restrictions
30*a33f8e80Stracker-user        $prefix  = '';
31*a33f8e80Stracker-user        $namelen = strlen($name);
32*a33f8e80Stracker-user        if ($namelen > 100) {
33*a33f8e80Stracker-user            $file = basename($name);
34*a33f8e80Stracker-user            $dir  = dirname($name);
35*a33f8e80Stracker-user            if (strlen($file) > 100 || strlen($dir) > 155) {
36*a33f8e80Stracker-user                // we're still too large, let's use GNU longlink
37*a33f8e80Stracker-user                $this->writeRawFileHeader('././@LongLink', 0, 0, 0, $namelen, 0, 'L');
38*a33f8e80Stracker-user                for ($s = 0; $s < $namelen; $s += 512) {
39*a33f8e80Stracker-user                    $this->writebytes(pack("a512", substr($name, $s, 512)));
40*a33f8e80Stracker-user                }
41*a33f8e80Stracker-user                $name = substr($name, 0, 100); // cut off name
42*a33f8e80Stracker-user            } else {
43*a33f8e80Stracker-user                // we're fine when splitting, use POSIX ustar
44*a33f8e80Stracker-user                $prefix = $dir;
45*a33f8e80Stracker-user                $name   = $file;
46*a33f8e80Stracker-user            }
47*a33f8e80Stracker-user        }
48*a33f8e80Stracker-user
49*a33f8e80Stracker-user        // values are needed in octal
50*a33f8e80Stracker-user        $uid   = sprintf("%6s ", decoct($uid));
51*a33f8e80Stracker-user        $gid   = sprintf("%6s ", decoct($gid));
52*a33f8e80Stracker-user        $perm  = sprintf("%6s ", decoct($perm));
53*a33f8e80Stracker-user        $size  = self::numberEncode($size, 12);
54*a33f8e80Stracker-user        $mtime = self::numberEncode($mtime, 12);   // patched: was numberEncode($size, 12)
55*a33f8e80Stracker-user
56*a33f8e80Stracker-user        $data_first = pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime);
57*a33f8e80Stracker-user        $data_last  = pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, "");
58*a33f8e80Stracker-user
59*a33f8e80Stracker-user        for ($i = 0, $chks = 0; $i < 148; $i++) {
60*a33f8e80Stracker-user            $chks += ord($data_first[$i]);
61*a33f8e80Stracker-user        }
62*a33f8e80Stracker-user
63*a33f8e80Stracker-user        for ($i = 156, $chks += 256, $j = 0; $i < 512; $i++, $j++) {
64*a33f8e80Stracker-user            $chks += ord($data_last[$j]);
65*a33f8e80Stracker-user        }
66*a33f8e80Stracker-user
67*a33f8e80Stracker-user        $this->writebytes($data_first);
68*a33f8e80Stracker-user
69*a33f8e80Stracker-user        $chks = pack("a8", sprintf("%6s ", decoct($chks)));
70*a33f8e80Stracker-user        $this->writebytes($chks.$data_last);
71*a33f8e80Stracker-user    }
72*a33f8e80Stracker-user}
73