Login   Register  
PHP Classes
elePHPant
Icontem

Some fixes

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us

      TAR File Manager  >  All threads  >  Some fixes  >  (Un) Subscribe thread alerts  
Subject:Some fixes
Summary:Fixes for TAR creation
Messages:2
Author:pdontthink
Date:2010-06-06 23:25:51
Update:2010-06-18 19:37:49
 

  1. Some fixes   Reply   Report abuse  
Picture of pdontthink
pdontthink
2010-06-06 23:25:51
When adapting this to my needs, I had to fix a few things and add some features, the most important being the ability to handle filenames longer than 100 characters as well as the ability to add files by handing over the file content instead of pointing to a file on the local file system. Below is a unified diff of my changes against version 2.2 from 5/7/2002. Sadly, the formatting will probably get munged. Is there a "code" tag for these forums?



--- tar.class.php.orig 2010-06-04 10:48:18.000000000 -0700
+++ tar.class.php 2010-06-04 10:51:49.000000000 -0700
@@ -15,6 +15,7 @@

Usage:
Copyright (C) 2002 Josh Barger
+ Copyright (C) 2010 Paul Lesniewski <paul@squirrelmail.org>

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -59,6 +60,13 @@
- Changed "private" functions to have
special function names beginning with
two underscores
+ 3.0 06/06/2010 - Fixed some PHP notices/warnings
+ Add user and group names if supported
+ Added ability to handle long file
+ names (either GNU or USTAR/POSIX)
+ Added ability to add "virtual" files
+ whose actual contents are given
+ instead of a local file path
=======================================================================
*/

@@ -85,6 +93,7 @@
// to try to ensure valid file
// PRIVATE ACCESS FUNCTION
function __computeUnsignedChecksum($bytestring) {
+ $unsigned_chksum = 0;
for($i=0; $i<512; $i++)
$unsigned_chksum += ord($bytestring[$i]);
for($i=0; $i<8; $i++)
@@ -232,88 +241,222 @@
}


+ // Generates and returns a TAR header
+ // PRIVATE ACCESS FUNCTION
+ function __generateHeader($information, $prefix, $suffix, $type, $gnuExtended) {
+ // Generate tar header
+ // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
+ $header = '';
+ $header .= str_pad($suffix,100,chr(0));
+ $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information['size']),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_repeat(" ",8);
+ $header .= $type; // 0 = regular file, 5 = dir, L = next is long
+ $header .= str_repeat(chr(0),100);
+ if ($gnuExtended) {
+ $header .= str_pad("ustar",6,chr(32));
+ $header .= chr(32) . chr(0);
+ } else { // USTAR/POSIX
+ $header .= str_pad("ustar",6,chr(0));
+ $header .= "00";
+ }
+ $header .= str_pad($information["user_name"],32,chr(0));
+ $header .= str_pad($information["group_name"],32,chr(0));
+ $header .= str_repeat(chr(0),8);
+ $header .= str_repeat(chr(0),8);
+ $header .= str_pad($prefix,155,chr(0));
+ $header .= str_repeat(chr(0),12);
+
+ // Compute header checksum
+ $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
+ for($i=0; $i<6; $i++) {
+ $header[(148 + $i)] = substr($checksum,$i,1);
+ }
+ $header[154] = chr(0);
+ $header[155] = chr(32);
+
+ return $header;
+ }
+
+
// Generates a TAR file from the processed data
// PRIVATE ACCESS FUNCTION
- function __generateTAR() {
+ function __generateTar($gnuExtended=TRUE) {
// Clear any data currently in $this->tar_file
unset($this->tar_file);

// Generate Records for each directory, if we have directories
if($this->numDirectories > 0) {
foreach($this->directories as $key => $information) {
- unset($header);

- // Generate tar header for this directory
- // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
- $header .= str_pad($information["name"],100,chr(0));
- $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_repeat(" ",8);
- $header .= "5";
- $header .= str_repeat(chr(0),100);
- $header .= str_pad("ustar",6,chr(32));
- $header .= chr(32) . chr(0);
- $header .= str_pad("",32,chr(0));
- $header .= str_pad("",32,chr(0));
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),155);
- $header .= str_repeat(chr(0),12);
-
- // Compute header checksum
- $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
- for($i=0; $i<6; $i++) {
- $header[(148 + $i)] = substr($checksum,$i,1);
+ // deal with long file names using GNU extensions
+ //
+ if ($gnuExtended) {
+
+ // add long names in multiple header
+ // fields that preceed actual file data
+ //
+ // follows the GNU extension to the TAR
+ // standard that adds extra header fields,
+ // enough to accomodate any length paths
+ //
+ if (strlen($information['name']) > 100) {
+
+ // we assume no mbstring overloading of string fxns!
+
+ // create ././@LongLink header that
+ // indicates length of filename
+ //
+ $filename_len = strlen($information['name']);
+ $this->tar_file .= $this->__generateHeader(array(
+ 'size' => $filename_len,
+ 'time' => 0,
+ 'mode' => 0,
+ 'user_id' => 0,
+ 'user_name' => $information['user_name'],
+ 'group_id' => 0,
+ 'group_name' => $information['group_name'],
+ ),
+ '', '././@LongLink', 'L', TRUE);
+
+
+ // now spit out the filename, making
+ // sure to keep it 512-byte-alligned
+ //
+ $this->tar_file .= str_pad($information['name'], (ceil($filename_len / 512) * 512), chr(0));
+
+
+ // use first 100 chars for content header
+ //
+ $suffix = substr($information['name'], 0, 100);
+ $prefix = '';
+
+ } else {
+ $prefix = '';
+ $suffix = $information['name'];
+ }
+
+
+ // deal with long file names using USTAR/POSIX extensions
+ //
+ } else {
+
+ // break long names into chunks to fit
+ // in extended ustar header fields
+ //
+ // follows the USTAR extended POSIX standard
+ // that adds a prefix field, which is still
+ // only allows paths of up to 256 characters
+ //
+ if (strlen($information['name']) > 100) {
+ // we assume no mbstring overloading of string fxns!
+ $prefix = substr($information['name'], 0, 155);
+//TODO: path separator below may not work under all O/Ses(?)
+ $prefix = substr($prefix, 0, strrpos($prefix, '/'));
+ $prefix_len = strlen($prefix);
+ if ($prefix_len === 0) $prefix_len = -1;
+ $suffix = substr($information['name'], $prefix_len + 1, 100); // assues only one slash
+ } else {
+ $prefix = '';
+ $suffix = $information['name'];
+ }
}
- $header[154] = chr(0);
- $header[155] = chr(32);

- // Add new tar formatted data to tar file contents
- $this->tar_file .= $header;
+ // Generate tar header for this directory
+ $information['user_name'] = '';
+ $information['group_name'] = '';
+ $information['size'] = 0;
+ $this->tar_file .= $this->__generateHeader($information, $prefix, $suffix, '5', $gnuExtended);
}
}

// Generate Records for each file, if we have files (We should...)
if($this->numFiles > 0) {
foreach($this->files as $key => $information) {
- unset($header);

- // Generate the TAR header for this file
- // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
- $header .= str_pad($information["name"],100,chr(0));
- $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_repeat(" ",8);
- $header .= "0";
- $header .= str_repeat(chr(0),100);
- $header .= str_pad("ustar",6,chr(32));
- $header .= chr(32) . chr(0);
- $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP?
- $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP?
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),155);
- $header .= str_repeat(chr(0),12);
-
- // Compute header checksum
- $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
- for($i=0; $i<6; $i++) {
- $header[(148 + $i)] = substr($checksum,$i,1);
+ // deal with long file names using GNU extensions
+ //
+ if ($gnuExtended) {
+
+ // add long names in multiple header
+ // fields that preceed actual file data
+ //
+ // follows the GNU extension to the TAR
+ // standard that adds extra header fields,
+ // enough to accomodate any length paths
+ //
+ if (strlen($information['name']) > 100) {
+
+ // we assume no mbstring overloading of string fxns!
+
+ // create ././@LongLink header that
+ // indicates length of filename
+ //
+ $filename_len = strlen($information['name']);
+ $this->tar_file .= $this->__generateHeader(array(
+ 'size' => $filename_len,
+ 'time' => 0,
+ 'mode' => 0,
+ 'user_id' => 0,
+ 'user_name' => $information['user_name'],
+ 'group_id' => 0,
+ 'group_name' => $information['group_name'],
+ ),
+ '', '././@LongLink', 'L', TRUE);
+
+
+ // now spit out the filename, making
+ // sure to keep it 512-byte-alligned
+ //
+ $this->tar_file .= str_pad($information['name'], (ceil($filename_len / 512) * 512), chr(0));
+
+
+ // use first 100 chars for content header
+ //
+ $suffix = substr($information['name'], 0, 100);
+ $prefix = '';
+
+ } else {
+ $prefix = '';
+ $suffix = $information['name'];
+ }
+
+
+ // deal with long file names using USTAR/POSIX extensions
+ //
+ } else {
+
+ // break long names into chunks to fit
+ // in extended ustar header fields
+ //
+ // follows the USTAR extended POSIX standard
+ // that adds a prefix field, which is still
+ // only allows paths of up to 256 characters
+ //
+ if (strlen($information['name']) > 100) {
+ // we assume no mbstring overloading of string fxns!
+ $prefix = substr($information['name'], 0, 155);
+//TODO: path separator below may not work under all O/Ses(?)
+ $prefix = substr($prefix, 0, strrpos($prefix, '/'));
+ $prefix_len = strlen($prefix);
+ if ($prefix_len === 0) $prefix_len = -1;
+ $suffix = substr($information['name'], $prefix_len + 1, 100); // assues only one slash
+ } else {
+ $prefix = '';
+ $suffix = $information['name'];
+ }
}
- $header[154] = chr(0);
- $header[155] = chr(32);

+ // Generate the TAR header for this file
+ $this->tar_file .= $this->__generateHeader($information, $prefix, $suffix, '0', $gnuExtended);
// Pad file contents to byte count divisible by 512
$file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0));

// Add new tar formatted data to tar file contents
- $this->tar_file .= $header . $file_contents;
+ $this->tar_file .= $file_contents;
}
}

@@ -428,7 +571,7 @@
$activeDir["time"] = $file_information["time"];
$activeDir["user_id"] = $file_information["uid"];
$activeDir["group_id"] = $file_information["gid"];
- $activeDir["checksum"] = $checksum;
+ $activeDir["checksum"] = isset($checksum) ? $checksum : '';

return true;
}
@@ -461,11 +604,51 @@
$activeFile["group_id"] = $file_information["gid"];
$activeFile["size"] = $file_information["size"];
$activeFile["time"] = $file_information["mtime"];
- $activeFile["checksum"] = $checksum;
+ $activeFile["checksum"] = isset($checksum) ? $checksum : '';
$activeFile["user_name"] = "";
$activeFile["group_name"] = "";
$activeFile["file"] = $file_contents;

+ if (function_exists('posix_getpwuid')
+ && ($owner = @posix_getpwuid($file_information['uid']))
+ && !empty($owner['name']))
+ $activeFile['user_name'] = $owner['name'];
+ if (function_exists('posix_getgrgid')
+ && ($group = @posix_getgrgid($file_information['gid']))
+ && !empty($group['name']))
+ $activeFile['group_name'] = $group['name'];
+
+
+ return true;
+ }
+
+
+ // Add a file to the tar archive that is in memory; not from the local file system
+ function addVirtualFile($file_contents, $filename, $file_mtime=0, $file_mode=0440, $file_uid=0, $file_gid=0, $file_size=0, $file_owner='', $file_group='') {
+
+ if ($file_mtime === 0) $file_mtime = time();
+
+ // default file size... assumes strlen isn't mbstring-overloaded
+ if ($file_size === 0) $file_size = strlen($file_contents);
+
+ // Make sure there are no other files in the archive that have this same filename
+ if($this->containsFile($filename))
+ return false;
+
+ // Add file to processed data
+ $this->numFiles++;
+ $activeFile = &$this->files[];
+ $activeFile['name'] = $filename;
+ $activeFile['mode'] = $file_mode;
+ $activeFile['user_id'] = $file_uid;
+ $activeFile['group_id'] = $file_gid;
+ $activeFile['size'] = $file_size;
+ $activeFile['time'] = $file_mtime;
+ $activeFile['checksum'] = isset($checksum) ? $checksum : '';
+ $activeFile['user_name'] = $file_owner;
+ $activeFile['group_name'] = $file_group;
+ $activeFile['file'] = $file_contents;
+
return true;
}

@@ -503,24 +686,24 @@


// Write the currently loaded tar archive to disk
- function saveTar() {
+ function saveTar($gnuExtended=TRUE) {
if(!$this->filename)
return false;

// Write tar to current file using specified gzip compression
- $this->toTar($this->filename,$this->isGzipped);
+ $this->toTar($this->filename,$this->isGzipped,$gnuExtended);

return true;
}


// Saves tar archive to a different file than the current file
- function toTar($filename,$useGzip) {
+ function toTar($filename,$useGzip,$gnuExtended=TRUE) {
if(!$filename)
return false;

// Encode processed files into TAR file format
- $this->__generateTar();
+ $this->__generateTar($gnuExtended);

// GZ Compress the data if we need to
if($useGzip) {

  2. Re: Some fixes   Reply   Report abuse  
Picture of stasikiii
stasikiii
2010-06-18 19:37:49 - In reply to message 1 from pdontthink
hi! Good work. How add folder (w/subfolders and files) to archive? It's possible?