. * */ /** * 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');