"- sqlfs is now fully case-sensitiv (before it was partly case-insensitiv)

- symlinks are now read by url_stat and dir_opendir and stored in the stat cache, to minimize DB accesses
- negative url_stat calles (not found) are now stored in the stat cache too, to minimize DB access
- added new log level 3, which adds method-name and line number to the sql statements"
This commit is contained in:
Ralf Becker 2009-03-27 19:10:16 +00:00
parent 881225849d
commit 7f9aace528

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -76,6 +76,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
* 0 = Nothing
* 1 = only errors
* 2 = all function calls and errors (contains passwords too!)
* 3 = log line numbers in sql statements
*/
const LOG_LEVEL = 1;
@ -195,8 +196,10 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
return false;
}
// new file --> create it in the DB
$stmt = self::$pdo->prepare($query='INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size)');
$query = 'INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size)';
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
$stmt = self::$pdo->prepare($query);
$values = array(
'fs_name' => basename($path),
'fs_dir' => $dir_stat['ino'],
@ -231,7 +234,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
elseif(!file_exists($fs_dir=dirname(self::_fs_path($this->opened_fs_id))))
{
$umaskbefore = umask();
if (self::LOG_LEVEL) error_log(__METHOD__." about to call mkdir for $fs_dir # Present UMASK:".decoct($umaskbefore)." called from:".function_backtrace());
if (self::LOG_LEVEL > 1) error_log(__METHOD__." about to call mkdir for $fs_dir # Present UMASK:".decoct($umaskbefore)." called from:".function_backtrace());
self::mkdir_recursive($fs_dir,0700,true);
}
}
@ -339,6 +342,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
}
$ret = fclose($this->opened_stream) && $ret;
unset(self::$stat_cache[$this->opened_path]);
$this->opened_stream = $this->opened_path = $this->opened_mode = $this->opend_fs_id = null;
$this->operation = self::DEFAULT_OPERATION;
@ -580,6 +584,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
return false;
}
unset(self::$stat_cache[$path_from]);
unset(self::$stat_cache[$path_to]);
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_dir=:fs_dir,fs_name=:fs_name WHERE fs_id=:fs_id');
return $stmt->execute(array(
@ -654,6 +659,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
}
return false; // no permission or file does not exist
}
unset(self::$stat_cache[$path]);
$stmt = self::$pdo->prepare('INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified,fs_creator'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_size,:fs_mime,:fs_created,:fs_modified,:fs_creator)');
return $stmt->execute(array(
@ -751,6 +757,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
}
$stat = self::url_stat($path,0);
}
unset(self::$stat_cache[$path]);
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_modified=:fs_modified,fs_modifier=:fs_modifier WHERE fs_id=:fs_id');
return $stmt->execute(array(
@ -905,7 +912,10 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
return false;
}
$this->opened_dir = array();
$query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified FROM '.self::TABLE." WHERE fs_dir=? ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC";
$query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.
",CASE fs_mime WHEN '".self::SYMLINK_MIME_TYPE."' THEN fs_content ELSE NULL END AS readlink FROM ".self::TABLE.
" WHERE fs_dir=? ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC";
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
$stmt = self::$pdo->prepare($query);
$stmt->setFetchMode(PDO::FETCH_ASSOC);
@ -964,14 +974,16 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
// check if we already have the info from the last dir_open call, as the old vfs reads it anyway from the db
if (self::$stat_cache && isset(self::$stat_cache[$path]))
{
return self::_vfsinfo2stat(self::$stat_cache[$path]);
return self::$stat_cache[$path] ? self::_vfsinfo2stat(self::$stat_cache[$path]) : false;
}
if (!is_object(self::$pdo))
{
self::_pdo();
}
$base_query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified FROM '.self::TABLE.' WHERE fs_name=? AND fs_dir=';
$base_query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.
",CASE fs_mime WHEN '".self::SYMLINK_MIME_TYPE."' THEN fs_content ELSE NULL END AS readlink FROM ".self::TABLE.
' WHERE fs_name'.self::$case_sensitive_equal.'? AND fs_dir=';
$parts = explode('/',$path);
// if we have extendes acl access to the url, we dont need and can NOT include the sql for the readable check
@ -987,7 +999,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
}
elseif ($n < count($parts)-1)
{
$query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_name='.self::$pdo->quote($name);
$query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name);
// if we are not root AND have no extended acl access, we need to make sure the user has the right to tranverse all parent directories (read-rights)
if (!egw_vfs::$is_root && !$eacl_access)
@ -1003,17 +1015,20 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
}
else
{
$query = str_replace('fs_name=?','fs_name='.self::$pdo->quote($name),$base_query).'('.$query.')';
$query = str_replace('fs_name'.self::$case_sensitive_equal.'?','fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name),$base_query).'('.$query.')';
}
}
//$query = "/* sqlfs::url_stat($path) */ ".$query;
//echo "query=$query\n";
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
if (!($result = self::$pdo->query($query)) || !($info = $result->fetch(PDO::FETCH_ASSOC)))
{
self::_remove_password($url);
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags) file or directory not found!");
return false;
if (self::LOG_LEVEL > 1)
{
self::_remove_password($url);
error_log(__METHOD__."('$url',$flags) file or directory not found!");
}
// we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)
return self::$stat_cache[$path] = false;
}
self::$stat_cache[$path] = $info;
@ -1100,21 +1115,18 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
/**
* This method is called in response to readlink().
*
* The readlink value is read by url_stat or dir_opendir and therefore cached in the stat-cache.
*
* @param string $url
* @return string|boolean content of the symlink or false if $url is no symlink (or not found)
*/
static function readlink($url)
static function readlink($path)
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url')");
$link = !($lstat = self::url_stat($path,STREAM_URL_STAT_LINK)) || is_null($lstat['readlink']) ? false : $lstat['readlink'];
$url_path = parse_url($url,PHP_URL_PATH);
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = $link");
if (!($lstat = self::url_stat($url_path,STREAM_URL_STAT_LINK)) || !($lstat['mode'] & self::MODE_LINK) ||
!($result = self::$pdo->query("SELECT fs_content FROM ".self::TABLE." WHERE fs_id=".(int)$lstat['ino'])))
{
return false;
}
return $result->fetchColumn();
return $link;
}
/**
@ -1134,16 +1146,15 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$target','$link') returning false! (!stat('$link') || !is_writable('$dir'))");
return false; // $link already exists or parent dir does not
}
$stmt = self::$pdo->prepare($query='INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size,fs_content'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_content)');
$query = 'INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size,fs_content'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_content)';
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
$stmt = self::$pdo->prepare($query);
$values = array(
'fs_name' => basename($link),
'fs_dir' => $dir_stat['ino'],
// we use the mode of the dir, so files in group dirs stay accessible by all members
'fs_mode' => ($dir_stat['mode'] & 0666),
// for the uid we use the uid of the dir if not 0=root or the current user otherwise
'fs_uid' => $dir_stat['uid'] ? $dir_stat['uid'] : egw_vfs::$user,
// we allways use the group of the dir
'fs_gid' => $dir_stat['gid'],
'fs_created' => self::_pdo_timestamp(time()),
'fs_modified' => self::_pdo_timestamp(time()),
@ -1156,6 +1167,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
{
$stmt->bindParam(':'.$name,$val);
}
unset(self::$stat_cache[$link]);
return !!$stmt->execute();
}
@ -1354,6 +1367,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
if (count($ids) > 1) array_map(create_function('&$v','$v = (int)$v;'),$ids);
$query = 'SELECT fs_id,fs_dir,fs_name FROM '.self::TABLE.' WHERE fs_id'.
(count($ids) == 1 ? '='.(int)$ids[0] : ' IN ('.implode(',',$ids).')');
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
if (!is_object(self::$pdo))
{
@ -1411,14 +1425,21 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
'mtime' => strtotime($info['fs_modified']),
'ctime' => strtotime($info['fs_created']),
'nlink' => $info['fs_mime'] == self::DIR_MIME_TYPE ? 2 : 1,
// eGW addition to return the mime type
// eGW addition to return some extra values
'mime' => $info['fs_mime'],
'readlink' => $info['readlink'],
);
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($info[name]) = ".array2string($stat));
return $stat;
}
private static $pdo_type;
/**
* Case sensitive comparison operator, for mysql we use ' COLLATE utf8_bin ='
*
* @var string
*/
public static $case_sensitive_equal = '=';
/**
* Create pdo object / connection, as long as pdo is not generally used in eGW
@ -1433,6 +1454,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
{
case 'mysqli':
case 'mysqlt':
case 'mysql':
self::$case_sensitive_equal = ' COLLATE utf8_bin =';
self::$pdo_type = 'mysql';
break;
default:
@ -1531,7 +1554,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
$GLOBALS['egw_info']['server']['files_dir'] = $GLOBALS['egw_setup']->db->select('egw_config','config_value',array(
'config_name' => 'files_dir',
'config_app' => 'phpgwapi',
),__LINE__,__FILE__)->fetchSingle();
),__LINE__,__FILE__)->fetchColumn();
}
}
if (!$GLOBALS['egw_info']['server']['files_dir'])
@ -1673,7 +1696,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
{
if (!($stat = self::url_stat($id,0)))
{
if (self::LOG_LEVEL) error_log(__METHOD__."(".array2string($path_ids).",$depth,$ns) path '$id' not found!");
if (self::LOG_LEVEL) error_log(__METHOD__."(".array2string($path_ids).",$ns) path '$id' not found!");
return false;
}
$id = $stat['ino'];
@ -1683,6 +1706,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
$query = 'SELECT * FROM '.self::PROPS_TABLE.' WHERE (fs_id'.
(count($ids) == 1 ? '='.(int)$ids[0] : ' IN ('.implode(',',$ids).')').')'.
(!is_null($ns) ? ' AND prop_namespace=?' : '');
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
$stmt = self::$pdo->prepare($query);
$stmt->setFetchMode(PDO::FETCH_ASSOC);