diff options
author | Robin Appelman <icewind1991@gmail.com> | 2011-11-09 18:41:57 +0100 |
---|---|---|
committer | Robin Appelman <icewind1991@gmail.com> | 2011-11-09 18:41:57 +0100 |
commit | 5a6aba1e1114707942c613c2402eed3237b43a21 (patch) | |
tree | 5cda68cc0256db5a3b3002fa694026d7d1475128 /lib/filecache.php | |
parent | 5cc6635eb81a8d76c04ee040a791f51ebbe389ef (diff) | |
download | nextcloud-server-5a6aba1e1114707942c613c2402eed3237b43a21.tar.gz nextcloud-server-5a6aba1e1114707942c613c2402eed3237b43a21.zip |
provide caching for file metadata
Diffstat (limited to 'lib/filecache.php')
-rw-r--r-- | lib/filecache.php | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/lib/filecache.php b/lib/filecache.php new file mode 100644 index 00000000000..41e31b5de25 --- /dev/null +++ b/lib/filecache.php @@ -0,0 +1,331 @@ +<?php + +/** +* @author Robin Appelman +* @copyright 2011 Robin Appelman icewind1991@gmail.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * provide caching for filesystem info in the database + * + * not used by OC_Filesystem for reading filesystem info, + * instread apps should use OC_FileCache::get where possible + * + * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache + */ +class OC_FileCache{ + /** + * get the filesystem info from the cache + * @param string path + * @return array + * + * returns an assiciative array with the following keys: + * - size + * - mtime + * - ctime + * - mimetype + */ + public static function get($path){ + $path=OC_Filesystem::getRoot().$path; + $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size FROM *PREFIX*fscache WHERE path=?'); + $result=$query->execute(array($path))->fetchRow(); + if(is_array($result)){ + return $result; + }else{ + OC_Log::write('file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return false; + } + } + + /** + * put filesystem info in the cache + * @param string $path + * @param array data + * + * $data is an assiciative array in the same format as returned by get + */ + public static function put($path,$data){ + $path=OC_Filesystem::getRoot().$path; + if($id=self::getFileId($path)!=-1){ + self::update($id,$data); + } + if($path=='/'){ + $parent=-1; + }else{ + $parent=self::getFileId(dirname($path)); + } + $mimePart=dirname($data['mimetype']); + $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart) VALUES(?,?,?,?,?,?,?,?)'); + $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart)); + + } + + /** + * update filesystem info of a file + * @param int $id + * @param array $data + */ + private static function update($id,$data){ + $mimePart=dirname($data['mimetype']); + $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET size=? ,mtime=? ,ctime=? ,mimetype=? , mimepart=? WHERE id=?'); + $query->execute(array($data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$id)); + } + + /** + * register a file move in the cache + * @param string oldPath + * @param string newPath + */ + public static function move($oldPath,$newPath){ + $oldPath=OC_Filesystem::getRoot().$oldPath; + $newPath=OC_Filesystem::getRoot().$newPath; + $newParent=self::getParentId($newPath); + $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=? WHERE path=?'); + $query->execute(array($newParent,basename($newPath),$newPath,$oldPath)); + } + + /** + * delete info from the cache + * @param string $path + */ + public static function delete($path){ + $path=OC_Filesystem::getRoot().$path; + $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path=?'); + $query->execute(array($path)); + } + + /** + * return array of filenames matching the querty + * @param string $query + * @return array of filepaths + */ + public static function search($query){ + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE name LIKE ?'); + $result=$query->execute(array("%$query%")); + $names=array(); + while($row=$result->fetchRow()){ + $names[]=$row['path']; + } + return $names; + } + + /** + * get all files and folders in a folder + * @param string path + * @return array + * + * returns an array of assiciative arrays with the following keys: + * - name + * - size + * - mtime + * - ctime + * - mimetype + */ + public static function getFolderContent($path){ + $path=OC_Filesystem::getRoot().$path; + $parent=self::getFileId($path); + $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size FROM *PREFIX*fscache WHERE parent=?'); + $result=$query->execute(array($parent))->fetchAll(); + if(is_array($result)){ + return $result; + }else{ + OC_Log::write('file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return false; + } + } + + /** + * check if a file or folder is in the cache + * @param string $path + * @return bool + */ + public static function inCache($path){ + $path=OC_Filesystem::getRoot().$path; + $inCache=self::getFileId($path)!=-1; + return $inCache; + } + + /** + * get the file id as used in the cache + * @param string $path + * @return int + */ + private static function getFileId($path){ + $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?'); + $result=$query->execute(array($path))->fetchRow(); + if(is_array($result)){ + return $result['id']; + }else{ + OC_Log::write('file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return -1; + } + } + + /** + * get the file id of the parent folder, taking into account '/' has no parent + * @param string $path + * @return int + */ + private static function getParentId($path){ + if($path=='/'){ + return -1; + }else{ + return self::getFileId(dirname($path)); + } + } + + /** + * called when changes are made to files + */ + public static function fileSystemWatcherWrite($params){ + $path=$params['path']; + $fullPath=OC_Filesystem::getRoot().$path; + $mimetype=OC_Filesystem::getMimeType($path); + if($mimetype=='httpd/unix-directory'){ + $size=0; + }else{ + if(($id=self::getFileId($fullPath))!=-1){ + $oldInfo=self::get($fullPath); + $oldSize=$oldInfo['size']; + }else{ + $oldSize=0; + } + $size=OC_Filesystem::filesize($path); + self::increaseSize(dirname($fullPath),$size-$oldSize); + } + $mtime=OC_Filesystem::filemtime($path); + $ctime=OC_Filesystem::filectime($path); + self::put($path,array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype)); + } + + /** + * called when files are deleted + */ + public static function fileSystemWatcherDelete($params){ + $path=$params['path']; + $fullPath=OC_Filesystem::getRoot().$path; + error_log("delete $path"); + if(self::getFileId($fullPath)==-1){ + return; + } + $size=OC_Filesystem::filesize($path); + self::increaseSize(dirname($fullPath),-$size); + self::delete($path); + } + + /** + * called when files are deleted + */ + public static function fileSystemWatcherRename($params){ + $oldPath=$params['oldpath']; + $newPath=$params['newpath']; + $fullOldPath=OC_Filesystem::getRoot().$oldPath; + $fullNewPath=OC_Filesystem::getRoot().$newPath; + if(($id=self::getFileId($fullOldPath))!=-1){ + $oldInfo=self::get($fullOldPath); + $oldSize=$oldInfo['size']; + }else{ + return; + } + $size=OC_Filesystem::filesize($oldPath); + self::increaseSize(dirname($fullOldPath),-$oldSize); + self::increaseSize(dirname($fullNewPath),$oldSize); + self::move($oldPath,$newPath); + } + + /** + * adjust the size of the parent folders + * @param string $path + * @param int $sizeDiff + */ + private static function increaseSize($path,$sizeDiff){ + while(($id=self::getFileId($path))!=-1){ + $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET size=size+? WHERE id=?'); + error_log('diff '.$path.' '.$sizeDiff); + $query->execute(array($sizeDiff,$id)); + $path=dirname($path); + } + } + + /** + * recursively scan the filesystem and fill the cache + * @param string $path + * @param bool $onlyChilds + */ + public static function scan($path,$onlyChilds=false){//PROBLEM due to the order things are added, all parents are -1 + $dh=OC_Filesystem::opendir($path); + $stat=OC_Filesystem::stat($path); + $mimetype=OC_Filesystem::getMimeType($path); + $stat['mimetype']=$mimetype; + if($path=='/'){ + $path=''; + } + self::put($path,$stat); + $fullPath=OC_Filesystem::getRoot().$path; + $totalSize=0; + if($dh){ + while (($filename = readdir($dh)) !== false) { + if($filename != '.' and $filename != '..'){ + $file=$path.'/'.$filename; + if(OC_Filesystem::is_dir($file)){ + self::scan($file,true); + }else{ + $stat=OC_Filesystem::stat($file); + $mimetype=OC_Filesystem::getMimeType($file); + $stat['mimetype']=$mimetype; + self::put($file,$stat); + $totalSize+=$stat['size']; + } + } + } + } + self::increaseSize($fullPath,$totalSize); + } + + /** + * fine files by mimetype + * @param string $part1 + * @param string $part2 (optional) + * @return array of file paths + * + * $part1 and $part2 together form the complete mimetype. + * e.g. searchByMime('text','plain') + * + * seccond mimetype part can be ommited + * e.g. searchByMime('audio') + */ + public static function searchByMime($part1,$part2=''){ + if($part2){ + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimepart=?'); + $result=$query->execute(array($part1)); + }else{ + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimetype=?'); + $result=$query->execute(array($part1.'/'.$part2)); + } + $names=array(); + while($row=$result->fetchRow()){ + $names[]=$row['path']; + } + return $names; + } +} + +//watch for changes and try to keep the cache up to date +OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache','fileSystemWatcherWrite'); +OC_Hook::connect('OC_Filesystem','delete','OC_FileCache','fileSystemWatcherDelete'); +OC_Hook::connect('OC_Filesystem','rename','OC_FileCache','fileSystemWatcherRename'); + |