PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Archive / Header / Header.php
backup / src / JetBackup / Archive / Header Last commit date
Sparse 1 year ago .htaccess 1 year ago Header.php 1 year ago index.html 1 year ago web.config 1 year ago
Header.php
351 lines
1 <?php
2 /*
3 *
4 * JetBackup @ package
5 * Created By Idan Ben-Ezra
6 *
7 * Copyrights @ JetApps
8 * https://www.jetapps.com
9 *
10 **/
11 namespace JetBackup\Archive\Header;
12
13 use JetBackup\Archive\Archive;
14 use JetBackup\Archive\Data\SetterGetter;
15 use JetBackup\Archive\Header\Sparse\Sparse;
16 use JetBackup\Archive\Header\Sparse\SparseRegion;
17 use JetBackup\Exception\ArchiveException;
18
19 class Header extends SetterGetter {
20
21 const REGTYPE = '0'; // regular file
22 const AREGTYPE = '\0'; // regular file
23 const LNKTYPE = '1'; // link
24 const SYMTYPE = '2'; // reserved (symlink)
25 const CHRTYPE = '3'; // character special
26 const BLKTYPE = '4'; // block special
27 const DIRTYPE = '5'; // directory
28 const FIFOTYPE = '6'; // FIFO special
29 const CONTTYPE = '7'; // reserved
30 const XHDTYPE = 'x'; // Extended header referring to the next file in the archive
31 const XGLTYPE = 'g'; // Global extended header
32
33 /* This is a dir entry that contains the names of files that were in the
34 dir at the time the dump was made. */
35 const GNUTYPE_DUMPDIR = 'D';
36
37 /* Identifies the *next* file on the tape as having a long linkname. */
38 const GNUTYPE_LONGLINK = 'K';
39
40 /* Identifies the *next* file on the tape as having a long name. */
41 const GNUTYPE_LONGNAME = 'L';
42
43 /* This is the continuation of a file that began on another volume. */
44 const GNUTYPE_MULTIVOL = 'M';
45
46 /* This is for sparse files. */
47 const GNUTYPE_SPARSE = 'S';
48
49 /* This file is a tape/volume header. Ignore it on extraction. */
50 const GNUTYPE_VOLHDR = 'V';
51
52 /* Solaris extended header */
53 const SOLARIS_XHDTYPE = 'X';
54
55 const LENGTH_FILENAME = 100;
56 const LENGTH_MODE = 8;
57 const LENGTH_UID = 8;
58 const LENGTH_GID = 8;
59 const LENGTH_SIZE = 12;
60 const LENGTH_MTIME = 12;
61 const LENGTH_CHECKSUM = 8;
62 const LENGTH_TYPEFLAG = 1;
63 const LENGTH_LINKNAME = 100;
64 const LENGTH_MAGIC = 6;
65 const LENGTH_VERSION = 2;
66 const LENGTH_UNAME = 32;
67 const LENGTH_GNAME = 32;
68 const LENGTH_DEVMAJOR = 8;
69 const LENGTH_DEVMINOR = 8;
70 const LENGTH_PREFIX = 155;
71 const LENGTH_PAD = 12;
72
73 const OFFSET_FILENAME = 0;
74 const OFFSET_MODE = self::LENGTH_FILENAME;
75 const OFFSET_UID = self::LENGTH_FILENAME + self::LENGTH_MODE;
76 const OFFSET_GID = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID;
77 const OFFSET_SIZE = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID;
78 const OFFSET_MTIME = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
79 self::LENGTH_SIZE;
80 const OFFSET_CHECKSUM = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
81 self::LENGTH_SIZE + self::LENGTH_MTIME;
82 const OFFSET_TYPEFLAG = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
83 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM;
84 const OFFSET_LINKNAME = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
85 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG;
86 const OFFSET_MAGIC = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
87 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
88 self::LENGTH_LINKNAME;
89 const OFFSET_VERSION = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
90 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
91 self::LENGTH_LINKNAME + self::LENGTH_MAGIC;
92 const OFFSET_UNAME = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
93 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
94 self::LENGTH_LINKNAME + self::LENGTH_MAGIC + self::LENGTH_VERSION;
95 const OFFSET_GNAME = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
96 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
97 self::LENGTH_LINKNAME + self::LENGTH_MAGIC + self::LENGTH_VERSION + self::LENGTH_UNAME;
98 const OFFSET_DEVMAJOR = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
99 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
100 self::LENGTH_LINKNAME + self::LENGTH_MAGIC + self::LENGTH_VERSION + self::LENGTH_UNAME +
101 self::LENGTH_GNAME;
102 const OFFSET_DEVMINOR = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
103 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
104 self::LENGTH_LINKNAME + self::LENGTH_MAGIC + self::LENGTH_VERSION + self::LENGTH_UNAME +
105 self::LENGTH_GNAME + self::LENGTH_DEVMAJOR;
106 const OFFSET_PREFIX = self::LENGTH_FILENAME + self::LENGTH_MODE + self::LENGTH_UID + self::LENGTH_GID +
107 self::LENGTH_SIZE + self::LENGTH_MTIME + self::LENGTH_CHECKSUM + self::LENGTH_TYPEFLAG +
108 self::LENGTH_LINKNAME + self::LENGTH_MAGIC + self::LENGTH_VERSION + self::LENGTH_UNAME +
109 self::LENGTH_GNAME + self::LENGTH_DEVMAJOR + self::LENGTH_DEVMINOR;
110
111 const
112 FILENAME = 'filename',
113 MODE = 'mode',
114 UID = 'uid',
115 GID = 'gid',
116 SIZE = 'size',
117 MTIME = 'mtime',
118 CHECKSUM = 'checksum',
119 TYPEFLAG = 'typeflag',
120 LINKNAME = 'linkname',
121 MAGIC = 'magic',
122 VERSION = 'version',
123 UNAME = 'uname',
124 GNAME = 'gname',
125 DEVMAJOR = 'devmajor',
126 DEVMINOR = 'devminor',
127 PREFIX = 'prefix',
128 SPARSE = 'sparse';
129
130 public function __construct($data=[]) {
131 parent::__construct();
132 if(isset($data[self::FILENAME])) $this->setFilename($data[self::FILENAME]);
133 if(isset($data[self::MODE])) $this->setMode($data[self::MODE]);
134 if(isset($data[self::UID])) $this->setUid($data[self::UID]);
135 if(isset($data[self::GID])) $this->setGid($data[self::GID]);
136 if(isset($data[self::SIZE])) $this->setSize($data[self::SIZE]);
137 if(isset($data[self::MTIME])) $this->setMtime($data[self::MTIME]);
138 if(isset($data[self::CHECKSUM])) $this->setChecksum($data[self::CHECKSUM]);
139 if(isset($data[self::TYPEFLAG])) $this->setTypeFlag($data[self::TYPEFLAG]);
140 if(isset($data[self::LINKNAME])) $this->setLinkName($data[self::LINKNAME]);
141 if(isset($data[self::MAGIC])) $this->setMagic($data[self::MAGIC]);
142 if(isset($data[self::VERSION])) $this->setVersion($data[self::VERSION]);
143 if(isset($data[self::UNAME])) $this->setUname($data[self::UNAME]);
144 if(isset($data[self::GNAME])) $this->setGname($data[self::GNAME]);
145 if(isset($data[self::DEVMAJOR])) $this->setDevMajor($data[self::DEVMAJOR]);
146 if(isset($data[self::DEVMINOR])) $this->setDevMinor($data[self::DEVMINOR]);
147 if(isset($data[self::PREFIX])) $this->setPrefix($data[self::PREFIX]);
148 if(isset($data[self::SPARSE])) $this->setSparse(new Sparse($data[self::SPARSE]));
149 }
150
151 public function setFilename($filename): void { $this->set(self::FILENAME, $filename); }
152 public function getFilename(): string { return $this->get(self::FILENAME); }
153
154 public function setMode($mode, $octal=true): void {
155
156
157 $this->set(self::MODE, $octal ? octdec(trim($mode)) : (int) $mode);
158
159 }
160 public function getMode($octal=true) { return self::_getDecOct($this->get(self::MODE), $octal, 7); }
161
162 public function setUid($uid, $octal=true): void { $this->set(self::UID, $octal ? octdec(trim($uid)) : (int) $uid); }
163 public function getUid($octal=true) { return self::_getDecOct($this->get(self::UID), $octal, 7); }
164
165 public function setGid($gid, $octal=true): void { $this->set(self::GID, $octal ? octdec(trim($gid)) : (int) $gid); }
166 public function getGid($octal=true) { return self::_getDecOct($this->get(self::GID), $octal, 7); }
167
168 public function setSize($size, $octal=true): void {
169
170 $this->set(self::SIZE, $octal ? octdec(trim($size)) : (int) $size);
171
172 }
173 public function getSize($octal=true) { return self::_getDecOct($this->get(self::SIZE), $octal, 11); }
174
175 public function setMtime($mtime, $octal=true): void { $this->set(self::MTIME, $octal ? octdec(trim($mtime)) : (int) $mtime); }
176 public function getMtime($octal=true) { return self::_getDecOct($this->get(self::MTIME), $octal, 11); }
177
178 public function setChecksum($checksum, $octal=true): void { $this->set(self::CHECKSUM, $octal ? octdec(trim($checksum)) : (int) $checksum); }
179 public function getChecksum($octal=true) { return self::_getDecOct($this->get(self::CHECKSUM), $octal, 6); }
180
181 public function setTypeFlag(string $flag): void { $this->set(self::TYPEFLAG, $flag); }
182 public function getTypeFlag(): string { return trim($this->get(self::TYPEFLAG)); }
183
184 public function setLinkName($name): void { $this->set(self::LINKNAME, $name); }
185 public function getLinkName(): string { return trim($this->get(self::LINKNAME)); }
186
187 public function setMagic($magic): void { $this->set(self::MAGIC, $magic); }
188 public function getMagic(): string { return trim($this->get(self::MAGIC)); }
189
190 public function setVersion($version): void { $this->set(self::VERSION, $version); }
191 public function getVersion(): string { return trim($this->get(self::VERSION)); }
192
193 public function setUname($name): void { $this->set(self::UNAME, $name); }
194 public function getUname(): string { return trim($this->get(self::UNAME)); }
195
196 public function setGname($name): void { $this->set(self::GNAME, $name); }
197 public function getGname(): string { return trim($this->get(self::GNAME)); }
198
199 public function setDevMajor($device, $octal=true): void { $this->set(self::DEVMAJOR, $octal ? octdec(trim($device)) : (int) $device); }
200 public function getDevMajor($octal=true) { return self::_getDecOct($this->get(self::DEVMAJOR), $octal, 7); }
201
202 public function setDevMinor($device, $octal=true): void { $this->set(self::DEVMINOR, $octal ? octdec(trim($device)) : (int) $device); }
203 public function getDevMinor($octal=true) { return self::_getDecOct($this->get(self::DEVMINOR), $octal, 7); }
204
205 public function setPrefix($prefix): void { $this->set(self::PREFIX, $prefix); }
206 public function getPrefix(): string { return $this->get(self::PREFIX); }
207
208 public function setSparse( ?Sparse $sparse=null): void { $this->set(self::SPARSE, $sparse); }
209 public function getSparse():?Sparse { return $this->get(self::SPARSE, null); }
210
211 /**
212 * @return void
213 */
214 public function buildPrefix(): void {
215 if($this->getTypeFlag() != self::GNUTYPE_SPARSE || !($sparse = $this->getSparse())) return;
216
217 $regions = $sparse->getRegions();
218
219 $prefix = "";
220 $prefix .= $sparse->getAtime();
221 $prefix .= $sparse->getCtime();
222 $prefix .= $sparse->getOffset();
223 $prefix .= $sparse->getLongName();
224 $prefix .= Archive::NULL_CHAR; // pad
225
226 // we have space only for 4 regions, more than that will be written in the next blocks (each 21 regions block)
227 for($i = 0; $i < 4; $i++) {
228 $region = array_shift($regions);
229
230 if($region) {
231 $prefix .= $region->getOffset();
232 $prefix .= $region->getNumbytes();
233 } else {
234 $prefix .= str_repeat(Archive::NULL_CHAR, SparseRegion::REGION_LENGTH);
235 }
236 }
237
238 $prefix .= sizeof($regions) ? Archive::TRUE_CHAR : Archive::NULL_CHAR; // extended
239 $prefix .= $sparse->getRealSize();
240 $prefix .= str_repeat(Archive::NULL_CHAR, Sparse::LENGTH_PAD_END);
241
242 $sparse->setRegions($regions);
243 $this->setPrefix($prefix);
244 }
245
246 /**
247 * @return string
248 */
249 public function pack(): string {
250 $pack = pack("a" . self::LENGTH_FILENAME, $this->getFilename()); //name
251 $pack .= pack("a" . self::LENGTH_MODE, $this->getMode()); // mode
252 $pack .= pack("a" . self::LENGTH_UID, $this->getUid()); // uid
253 $pack .= pack("a" . self::LENGTH_GID, $this->getGid()); // gid
254 $pack .= pack("a" . self::LENGTH_SIZE, $this->getSize()); // size
255 $pack .= pack("A" . self::LENGTH_MTIME, $this->getMtime()); // mtime
256 $pack .= pack("a" . self::LENGTH_CHECKSUM, ""); // checksum
257 $pack .= pack("a" . self::LENGTH_TYPEFLAG, $this->getTypeFlag()); // typeflag
258 $pack .= pack("a" . self::LENGTH_LINKNAME, $this->getLinkName()); // linkname
259 $pack .= pack("a" . self::LENGTH_MAGIC, $this->getMagic()); // magic
260 $pack .= pack("a" . self::LENGTH_VERSION, $this->getVersion()); // version
261 $pack .= pack("a" . self::LENGTH_UNAME, $this->getUname()); // uname
262 $pack .= pack("a" . self::LENGTH_GNAME, $this->getGname()); // gname
263 $pack .= pack("a" . self::LENGTH_DEVMAJOR, $this->getDevMajor()); // devmajor
264 $pack .= pack("a" . self::LENGTH_DEVMINOR, $this->getDevMinor()); // devminor
265 $pack .= pack("a" . self::LENGTH_PREFIX, $this->getPrefix()); // prefix
266 $pack .= pack("a" . self::LENGTH_PAD, ""); // pad
267
268 $this->setChecksum(self::calculateChecksum($pack), false);
269 $checksum = pack('a' . self::LENGTH_CHECKSUM, $this->getChecksum());
270 return substr_replace($pack, $checksum, self::OFFSET_CHECKSUM, self::LENGTH_CHECKSUM);
271 }
272
273 /**
274 * @param mixed $data
275 * @param callable $readDataBlock
276 *
277 * @return Header
278 * @throws ArchiveException
279 */
280 public static function parse($data, callable $readDataBlock, bool $debug=false): Header {
281
282 if($data === null || $data === false)
283 throw new ArchiveException("Failed reading header");
284
285 if(strlen($data) < Archive::BLOCK_SIZE)
286 throw new ArchiveException("Header length is invalid");
287
288 $args = [
289 "a" . self::LENGTH_FILENAME . self::FILENAME,
290 "a" . self::LENGTH_MODE . self::MODE,
291 "a" . self::LENGTH_UID . self::UID,
292 "a" . self::LENGTH_GID . self::GID,
293 "a" . self::LENGTH_SIZE . self::SIZE,
294 "A" . self::LENGTH_MTIME . self::MTIME,
295 "a" . self::LENGTH_CHECKSUM . self::CHECKSUM,
296 "a" . self::LENGTH_TYPEFLAG . self::TYPEFLAG,
297 "a" . self::LENGTH_LINKNAME . self::LINKNAME,
298 "a" . self::LENGTH_MAGIC . self::MAGIC,
299 "a" . self::LENGTH_VERSION . self::VERSION,
300 "a" . self::LENGTH_UNAME . self::UNAME,
301 "a" . self::LENGTH_GNAME . self::GNAME,
302 "a" . self::LENGTH_DEVMAJOR . self::DEVMAJOR,
303 "a" . self::LENGTH_DEVMINOR . self::DEVMINOR,
304 "a" . self::LENGTH_PREFIX . self::PREFIX
305 ];
306
307 if(!($header_data = unpack(implode("/", $args), $data)))
308 throw new ArchiveException("Failed parsing header");
309
310 if(isset($header_data[self::CHECKSUM]) && octdec(trim($header_data[self::CHECKSUM])) != self::calculateChecksum($data))
311 throw new ArchiveException("Header does not match its checksum for account '{$header_data[self::FILENAME]}'");
312
313 $header = new Header($header_data);
314
315 if($header->getTypeFlag() == Header::GNUTYPE_SPARSE)
316 $header->setSparse(Sparse::fromPrefix($header->getPrefix(), $readDataBlock));
317
318 if($debug) Archive::printHeader($header);
319
320 return $header;
321 }
322
323 /**
324 * @param string $data
325 *
326 * @return int
327 */
328 public static function calculateChecksum(string $data): int {
329 $checksum = 256;
330 for ($i = 0; $i < Archive::BLOCK_SIZE; $i++) {
331 // skip checksum, not should be in the checksum calculation
332 if($i >= self::OFFSET_CHECKSUM && $i < (self::OFFSET_CHECKSUM + self::LENGTH_CHECKSUM)) continue;
333 $checksum += ord($data[$i]);
334 }
335 return $checksum;
336 }
337
338 /**
339 * @param mixed $value
340 * @param bool $octal
341 * @param int $octal_length
342 *
343 * @return int|string
344 */
345 public static function _getDecOct( $value, bool $octal, int $octal_length=0) {
346 if(!$octal_length) $octal = false;
347 if(!$octal) return (int) $value;
348 if(!is_int($value)) return str_repeat(Archive::NULL_CHAR, $octal_length);
349 return sprintf("%0{$octal_length}o", $value);
350 }
351 }