From 3ada27bf3cf01c7a939dbe0640b1a34669d6c290 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 17 Mar 2010 09:13:46 +0000 Subject: [PATCH] ability to migrate DB based sqlfs to a filesystem based one using filemanager/cli.php (see usage) --- filemanager/cli.php | 32 +++++++- .../inc/class.sqlfs_stream_wrapper.inc.php | 78 ++++++++++++++++++- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/filemanager/cli.php b/filemanager/cli.php index f3ec304e8a..bb0e2d577e 100755 --- a/filemanager/cli.php +++ b/filemanager/cli.php @@ -6,7 +6,7 @@ * @link http://www.egroupware.org * @package filemanager * @author Ralf Becker - * @copyright (c) 2007/8 by Ralf Becker + * @copyright (c) 2007-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ * @@ -64,6 +64,7 @@ function usage($action=null,$ret=0) echo "\t$cmd mount URL [path] (without path prints out the mounts)\n"; echo "\t$cmd umount [-a|--all (restores default mounts)] URL|path\n"; echo "\t$cmd eacl URL [rwx-] [user or group]\n"; + echo "\tsudo -u apache $cmd migrate-db2fs --user root_admin --passwd password [--domain default] (migrates sqlfs content from DB to filesystem)\n"; echo "\nCommon options: --user user --password password [--domain domain] can be used to pass eGW credentials without using the URL writing.\n"; echo "\nURL: {vfs|sqlfs|filesystem}://user:password@domain/home/user/file[?option=value&...], /dir/file, ...\n"; @@ -251,6 +252,35 @@ switch($cmd) rename($argv[0],$argv[1]); break; + case 'migrate-db2fs': + if (empty($user) || empty($passwd) || !egw_vfs::$is_root) + { + die("\nYou need to be root to do that!\n\n"); + } + if (!is_writable($GLOBALS['egw_info']['server']['files_dir'])) exit; // we need write access, error msg already given + $fstab = egw_vfs::mount(); + if (!is_array($fstab) || !isset($fstab['/']) || strpos($fstab['/'],'storage=db') === false) + { + foreach($fstab as $path => $url) + { + echo "$url\t$path\n"; + } + die("\n/ NOT mounted with 'storage=db' --> no need to convert!\n\n"); + } + $num_files = sqlfs_stream_wrapper::migrate_db2fs(); // throws exception on error + echo "\n$num_files files migrated from DB to filesystem.\n"; + $new_url = preg_replace('/storage=db&?/','',$fstab['/']); + if (substr($new_url,-1) == '?') $new_url = substr($new_url,0,-1); + if (egw_vfs::mount($new_url,'/')) + { + echo "/ successful re-mounted on $new_url\n"; + } + else + { + echo "\nre-mounting $new_url on / failed!\n\n"; + } + break; + default: while($argv) { diff --git a/phpgwapi/inc/class.sqlfs_stream_wrapper.inc.php b/phpgwapi/inc/class.sqlfs_stream_wrapper.inc.php index 8200b399c6..d77cbde153 100644 --- a/phpgwapi/inc/class.sqlfs_stream_wrapper.inc.php +++ b/phpgwapi/inc/class.sqlfs_stream_wrapper.inc.php @@ -14,7 +14,7 @@ /** * eGroupWare API: VFS - new DB based VFS stream wrapper * - * The sqlfs stream wrapper has 3 operation modi: + * The sqlfs stream wrapper has 2 operation modi: * - content of files is stored in the filesystem (eGW's files_dir) (default) * - content of files is stored as BLOB in the DB (can be enabled by mounting sqlfs://...?storage=db) * please note the current (php5.2.6) problems: @@ -524,7 +524,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper if (($ret = $stmt->execute(array('fs_id' => $stat['ino'])))) { - if (self::url2operation($url) == self::STORE2FS && + if (self::url2operation($url) == self::STORE2FS && ($stat['mode'] & self::MODE_LINK) != self::MODE_LINK) { unlink(self::_fs_path($stat['ino'])); @@ -1779,6 +1779,80 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper if (self::LOG_LEVEL > 1) foreach((array)$props as $k => $v) error_log(__METHOD__."($path_ids,$ns) $k => ".array2string($v)); return $props; } + + /** + * Migrate SQLFS content from DB to filesystem + * + * @param boolean $debug true to echo a message for each copied file + */ + static function migrate_db2fs($debug=false) + { + if (!is_object(self::$pdo)) + { + self::_pdo(); + } + $query = 'SELECT fs_id,fs_name,fs_size,fs_content'. + ' FROM '.self::TABLE.' WHERE fs_content IS NOT NULL'; + + $stmt = self::$pdo->prepare($query); + $stmt->bindColumn(1,$fs_id); + $stmt->bindColumn(2,$fs_name); + $stmt->bindColumn(3,$fs_size); + $stmt->bindColumn(4,$fs_content,PDO::PARAM_LOB); + + if ($stmt->execute()) + { + foreach($stmt as $row) + { + // hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913) + // PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-( + if (is_string($fs_content)) + { + $name = md5($fs_name.$fs_id); + $GLOBALS[$name] =& $fs_content; + require_once(EGW_API_INC.'/class.global_stream_wrapper.inc.php'); + $content = fopen('global://'.$name,'r'); + if (!$content) echo "fopen('global://$name','w' failed, strlen(\$GLOBALS['$name'])=".strlen($GLOBALS[$name]).", \$GLOBALS['$name']=".substr($GLOBALS['$name'],0,100)."...\n"; + unset($GLOBALS[$name]); // unset it, so it does not use up memory, once the stream is closed + } + else + { + $content = $fs_content; + } + if (!is_resource($content)) + { + throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name, $fs_size bytes) content is NO resource! ".array2string($content)); + } + $filename = self::_fs_path($fs_id); + if (!file_exists($fs_dir=dirname($filename))) + { + self::mkdir_recursive($fs_dir,0700,true); + } + if (!($dest = fopen($filename,'w'))) + { + throw new egw_exception_assertion_failed(__METHOD__."(): fopen($filename,'w') failed!"); + } + if (($bytes = stream_copy_to_stream($content,$dest)) != $fs_size) + { + throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name) $bytes bytes copied != size of $fs_size bytes!"); + } + if ($debug) echo "$fs_id: $fs_name: $bytes bytes copied to fs\n"; + fclose($dest); + fclose($content); unset($content); + + ++$n; + } + unset($stmt); + + if ($n) // delete all content in DB, if there was some AND no error (exception thrown!) + { + $query = 'UPDATE '.self::TABLE.' SET fs_content=NULL WHERE fs_content IS NOT NULL'; + $stmt = self::$pdo->prepare($query); + $stmt->execute(); + } + } + return $n; + } } stream_register_wrapper(sqlfs_stream_wrapper::SCHEME ,'sqlfs_stream_wrapper');