[ Index ] |
PHP Cross Reference of PHK Manager |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================================= 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 //============================================================================= 17 /** 18 * @copyright Francois Laupretre <phk@tekwire.net> 19 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0 20 * @category PHK 21 * @package PHK 22 *///========================================================================== 23 24 namespace PHK { 25 26 if (!class_exists('PHK\Proxy',false)) 27 { 28 //============================================================================= 29 /** 30 * The 'back-end' object managing physical access to the package file. This 31 * object is created and called by the stream wrapper on cache misses. 32 * 33 * Runtime code -> 100% read-only except setBufferInterp(). 34 * 35 * API status: Private 36 * Included in the PHK PHP runtime: Yes 37 * Implemented in the extension: No 38 *///========================================================================== 39 40 class Proxy 41 { 42 //========== Class constants =============== 43 44 /** The size of the interp line (fixed size) */ 45 46 const INTERP_LEN=64; 47 48 /** The size of a version string in the magic block - Up to 12 bytes */ 49 50 const VERSION_SIZE=12; 51 52 /** The size of each offset field. As offset are limited to 11 bytes, the 53 * theoritical max size of a PHK archive is 100 G bytes. Not tested :) */ 54 55 const OFFSET_SIZE=11; 56 57 /** The magic string. This is the string we identify to recognize a PHK archive */ 58 59 const MAGIC_STRING="#PHK M\024\x8\6\3"; 60 const MAGIC_STRING_LEN=10; 61 62 /** INTERP_LEN + 6 */ 63 64 const MAGIC_STRING_OFFSET=70; 65 66 /** The size of the magic block */ 67 68 const MAGIC_LINE_LEN=177; 69 70 /** The name of the optional Automap section, when it exists */ 71 72 const AUTOMAP_SECTION='AUTOMAP'; 73 74 /** The offset of the CRC field, from the beginning of the archive file. 75 * The CRC field is 8 bytes long (32 bits in hexadecimal) */ 76 77 const CRC_OFFSET=200; 78 79 //========== Instance data =============== 80 81 /** @var string Package path */ 82 83 private $path; 84 85 /** @var PHK\Virtual\Tree Section tree */ 86 87 protected $stree=null; 88 89 /** @var PHK\Virtual\Tree File tree */ 90 91 public $ftree=null; 92 93 /** @var integer Mount flags */ 94 95 protected $flags; 96 97 /** @var PHK\PkgFileSpace File Handler */ 98 99 protected $fspace; 100 101 /** @var array Magic values */ 102 103 private $magic=null; 104 105 //========== Class methods =============== 106 107 /** 108 * Constructor 109 * 110 * This method must be called only from PHK\Mgr::proxy() 111 * 112 * @param string mount point 113 * 114 * @throws \Exception 115 */ 116 117 public function __construct($path,$flags) 118 { 119 try 120 { 121 Tools\Util::slowPath(); 122 123 //Tools\Util::trace("Starting proxy init");//TRACE 124 125 $this->path=$path; 126 $this->flags=$flags; 127 128 if (!($this->flags & \PHK::IS_CREATOR)) 129 { 130 // fileIsPackage() moved here from \PHK\Mgr::computeMnt() because we don't 131 // need to check this if data is already in cache. 132 133 if (! self::fileIsPackage($path)) 134 throw new \Exception($path.'is not a PHK package'); 135 136 $this->fspace= new PkgFileSpace($path,$flags); 137 $this->fspace->open(); 138 139 // Get magic block 140 141 $this->getMagicValues(); 142 143 // Check that file size corresponds to the value stored in the magic block. 144 // Done only once in slow path because, if the file size changes, the 145 // modification date will change too, and thus the mount point. 146 147 if ($this->fspace->size()!=$this->magic['fs']) // Check file size 148 Tools\Util::formatError('Invalid file size. Should be '.$this->magic['fs']); 149 150 // Import section tree 151 152 $this->stree=Virtual\Tree::createFromEdata( 153 $this->fspace->readBlock($this->magic['sso'] 154 ,$this->magic['sto']-$this->magic['sso']) 155 ,new PkgFileSpace($this->fspace,$this->magic['sto'] 156 ,$this->magic['fto']-$this->magic['sto'])); 157 158 $this->ftree=Virtual\Tree::createFromEdata($this->section('FTREE') 159 ,new PkgFileSpace($this->fspace,$this->magic['fto'] 160 ,$this->magic['sio']-$this->magic['fto'])); 161 162 $this->fspace->close(); // We keep the file open during init phase 163 } 164 else 165 { 166 $this->ftree=Virtual\Tree::createEmpty(); 167 $this->stree=Virtual\Tree::createEmpty(); 168 } 169 } 170 catch (\Exception $e) 171 { 172 throw new \Exception('While initializing PHK proxy - '.$e->getMessage()); 173 } 174 //Tools\Util::trace("Ending init - path=$path");//TRACE 175 } 176 177 //--------- 178 179 public function crcCheck() 180 { 181 try 182 { 183 self::checkCrcBuffer($this->fspace->readBlock()); 184 } 185 catch(\Exception $e) 186 { 187 throw new \Exception($this->path.': file is corrupted - '.$e->getMessage()); 188 } 189 } 190 191 //--------------------------------- 192 /** 193 * Inserts or clears a CRC in a memory buffer 194 * 195 * @static 196 * @param string $buffer The original buffer whose CRC will be overwritten 197 * @param string $crc If set, the CRC as an 8-char string (in hexadecimal). If 198 * not set, we clear the CRC (set it to '00000000'). 199 * @return string The modified buffer 200 */ 201 202 public static function insertCrc($buffer,$crc) 203 { 204 return substr_replace($buffer,$crc,self::CRC_OFFSET,8); 205 } 206 207 //-------------------------------- 208 /** 209 * Returns the CRC extracted from a memory buffer (not the computed one) 210 * 211 * @param string $buffer 212 * @return string The extracted 8-char hex CRC 213 */ 214 215 private static function getCrc($buffer) 216 { 217 return substr($buffer,self::CRC_OFFSET,8); 218 } 219 220 //--------------------------------- 221 /** 222 * Computes a CRC from a given memory buffer 223 * 224 * As the given buffer already contains a CRC, we first clear it. 225 * 226 * @param string $buffer 227 * @return string The computed 8-char hex CRC 228 */ 229 230 private static function computeCrc($buffer) 231 { 232 return hash('adler32',self::insertCrc($buffer,'00000000')); 233 } 234 235 //--------------------------------- 236 /** 237 * Checks a memory buffer's CRC 238 * 239 * The memory buffer is supposed to contain a whole PHK archive. 240 * 241 * No return value: if the CRC check fails, an exception is thrown. 242 * 243 * @param string $buffer 244 * @return void 245 * @throws \Exception 246 */ 247 248 public static function checkCrcBuffer($buffer) 249 { 250 if (self::computeCrc($buffer) !== self::getCrc($buffer)) 251 throw new \Exception('CRC check failed'); 252 } 253 254 //--------------------------------- 255 /** 256 * Computes and inserts a CRC in a memory buffer 257 * 258 * @param string $buffer 259 * @return string The modified buffer 260 */ 261 262 public static function fixCrc($buffer) 263 { 264 return self::insertCrc($buffer,self::computeCrc($buffer)); 265 } 266 267 //--------- 268 /** 269 * Check if a given path contains a PHK package 270 * 271 * @param string $path path to check (can be virtual) 272 * @return boolean 273 */ 274 275 public static function fileIsPackage($path) 276 { 277 if (filesize($path)< (self::INTERP_LEN+self::MAGIC_LINE_LEN)) return false; 278 if (($fp=fopen($path,'rb',false))===false) return false; 279 if (fseek($fp,self::MAGIC_STRING_OFFSET) != 0) return false; 280 if (($m=fread($fp,self::MAGIC_STRING_LEN))===false) return false; 281 fclose($fp); 282 return ($m===self::MAGIC_STRING); 283 } 284 285 //--------- 286 /** 287 * Check if a data buffer contains a PHK package 288 * 289 * @param string $data data buffer to check 290 * @return boolean 291 */ 292 293 public static function dataIsPackage($data) 294 { 295 if (strlen($data) < (self::INTERP_LEN+self::MAGIC_LINE_LEN)) return false; 296 return (substr($data,self::MAGIC_STRING_OFFSET,self::MAGIC_STRING_LEN) 297 ===self::MAGIC_STRING); 298 } 299 300 //--------------------------------- 301 /** 302 * Extracts the values out of a magic line buffer 303 * 304 * Note: A package is signed if (Signature offset != File size) 305 * 306 * @param string $buf A magic line content 307 * @return array An array containing the magic values 308 */ 309 310 public function getMagicValues() 311 { 312 $buf=$this->fspace->readBlock(self::INTERP_LEN,self::MAGIC_LINE_LEN); 313 314 $fsize=(int)substr($buf,47,self::OFFSET_SIZE); 315 $sio=(int)substr($buf,121,self::OFFSET_SIZE); 316 $crc=null; 317 sscanf(substr($buf,136,8),'%08x',$crc); 318 319 $this->magic=array( 320 'mv' => trim(substr($buf,18,self::VERSION_SIZE)), // Minimum required version 321 'v' => trim(substr($buf,32,self::VERSION_SIZE)), // Version 322 'fs' => $fsize, // File size 323 'po' => (int)substr($buf,61,self::OFFSET_SIZE), // Prolog offset 324 'sso' => (int)substr($buf,76,self::OFFSET_SIZE), // Serialized sections offset 325 'sto' => (int)substr($buf,91,self::OFFSET_SIZE), // Section table offset 326 'fto' => (int)substr($buf,106,self::OFFSET_SIZE), // File table offset 327 'sio' => $sio, // Signature offset 328 'pco' => (int)substr($buf,148,self::OFFSET_SIZE), // PHP code offset 329 'pcs' => (int)substr($buf,163,self::OFFSET_SIZE), // PHP code length 330 'crc' => $crc, 331 'signed' => ($sio != $fsize)); 332 } 333 334 //--------------------------------- 335 336 public function magicField($name) 337 { 338 return $this->magic[$name]; 339 } 340 341 //--------------------------------- 342 /** 343 * Brings all data in memory 344 * 345 * After this function has run, we never access the package file any more. 346 * 347 * @see \PHK\Virtual\DC implements the data cache 348 * 349 * @return void 350 */ 351 352 private function cacheData() 353 { 354 $this->stree->walk('read'); 355 $this->ftree->walk('read'); 356 } 357 358 //--------------------------------- 359 /** 360 * Clears the data cache 361 * 362 * @see \PHK\Virtual\DC implements the data cache 363 * 364 * @return void 365 */ 366 367 private function clearCache() 368 { 369 $this->stree->walk('clear_cache'); 370 $this->ftree->walk('clear_cache'); 371 } 372 373 //--------------------------------- 374 375 public function pathList() 376 { 377 return $this->ftree->pathList(); 378 } 379 380 //--------------------------------- 381 382 public function sectionList() 383 { 384 return $this->stree->pathList(); 385 } 386 387 388 //--------------------------------- 389 /** 390 * Is this package digitally signed ? 391 * 392 * @return boolean 393 */ 394 395 public function signed() 396 { 397 return $this->magic['signed']; 398 } 399 400 //----- 401 /** 402 * Gets interpreter string 403 * 404 * If the interpreter is defined, returns it. Else, returns an empty string 405 * 406 * @return string 407 * @throws \Exception if the interpreter string is invalid 408 */ 409 410 public function interp() 411 { 412 $block=$this->fspace->readBlock(0,self::INTERP_LEN); 413 414 if ((($block{0}!='#')||($block{1}!='!')) && (($block{0}!='<')||($block{1}!='?'))) 415 throw new \Exception('Invalid interpreter block'); 416 return ($block{0}=='#') ? trim(substr($block,2)) : ''; 417 } 418 419 //----- 420 /** 421 * Builds an interpreter block from an interpreter string 422 * 423 * Note: can be applied to a signed package as the signature ignores the 424 * interpreter block and the CRC. 425 426 * @param string $interp Interpreter to set or empty string to clear 427 * @return string Interpreter block (INTERP_LEN). Including trailing '\n' 428 */ 429 430 public static function interpBlock($interp) 431 { 432 if (($interp!=='') && (strlen($interp) > (\PHK\Proxy::INTERP_LEN-3))) 433 throw new \Exception('Length of interpreter string is limited to ' 434 .(\PHK\Proxy::INTERP_LEN-3).' bytes'); 435 436 // Keep '<?'.'php' or it will be translated when building the runtime code 437 438 if ($interp==='') return str_pad('<?'.'php',\PHK\Proxy::INTERP_LEN-2).'?'.'>'; 439 else return '#!'.str_pad($interp,\PHK\Proxy::INTERP_LEN-3)."\n"; 440 } 441 442 //----- 443 /** 444 * Inserts a new interpreter block in a file's content 445 * 446 * Allows a PHK user to change its interpreter string without 447 * having to use the \PHK\Build\Creator kit. 448 * 449 * Note: can be applied to a signed package as the signature ignores the 450 * interpreter block and the CRC. 451 * 452 * @param string $path PHK archive's path 453 * @param string $interp Interpreter string to set (empty to clear) 454 * @return string The modified buffer (the file is not overwritten) 455 */ 456 457 public static function setBufferInterp($path,$interp='') 458 { 459 return self::fixCrc(substr_replace(Tools\Util::readFile($path) 460 ,self::interpBlock($interp),0,\PHK\Proxy::INTERP_LEN)); 461 } 462 463 //----- 464 /** 465 * The version of the \PHK\Build\Creator tool this package was created from 466 * 467 * @return string Version 468 */ 469 470 public function version() 471 { 472 return $this->magic['v']; 473 } 474 475 //----- 476 /** 477 * Returns the $path property 478 * 479 * @return string 480 */ 481 482 public function path() 483 { 484 return $this->fspace->path(); 485 } 486 487 //----- 488 /** 489 * Get a section's content 490 * 491 * @param string $name The section name 492 * @return string The section's content 493 * @throws \Exception if section does not exist or cannot be read 494 */ 495 496 public function section($name) 497 { 498 try { $node=$this->stree->lookupFile($name); } 499 catch (\Exception $e) { throw new \Exception($name.': Unknown section'); } 500 501 try { return $node->read(); } 502 catch (\Exception $e) 503 { throw new \Exception($name.': Cannot read section - '.$e->getMessage()); } 504 } 505 506 //----- 507 508 public function ftree() 509 { 510 return $this->ftree; 511 } 512 513 //----- 514 515 public function stree() 516 { 517 return $this->stree; 518 } 519 520 //----- 521 /** 522 * Returns the $flags property 523 * 524 * @return integer 525 */ 526 527 public function flags() 528 { 529 return $this->flags; 530 } 531 532 //--------------------------------- 533 534 public function displayPackages() 535 { 536 $this->ftree->displayPackages(); 537 } 538 539 //--------------------------------- 540 // Display the file tree 541 542 public function showfiles() 543 { 544 $this->ftree->display(true); 545 } 546 547 //--- 548 } // End of class 549 //=========================================================================== 550 } // End of class_exists 551 //=========================================================================== 552 } // End of namespace 553 //=========================================================================== 554 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jun 4 18:33:15 2015 | Cross-referenced by PHPXref 0.7.1 |