filecache.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. <?php
  2. /**
  3. * @author Robin Appelman
  4. * @copyright 2011 Robin Appelman icewind1991@gmail.com
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public
  17. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. /**
  21. * provide caching for filesystem info in the database
  22. *
  23. * not used by OC_Filesystem for reading filesystem info,
  24. * instread apps should use OC_FileCache::get where possible
  25. *
  26. * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache
  27. */
  28. class OC_FileCache{
  29. private static $savedData=array();
  30. /**
  31. * get the filesystem info from the cache
  32. * @param string path
  33. * @param string root (optional)
  34. * @return array
  35. *
  36. * returns an assiciative array with the following keys:
  37. * - size
  38. * - mtime
  39. * - ctime
  40. * - mimetype
  41. * - encrypted
  42. * - versioned
  43. */
  44. public static function get($path,$root=''){
  45. if(self::isUpdated($path,$root)){
  46. if(!$root){//filesystem hooks are only valid for the default root
  47. OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path));
  48. }else{
  49. self::fileSystemWatcherWrite(array('path'=>$path),$root);
  50. }
  51. }
  52. if(!$root){
  53. $root=OC_Filesystem::getRoot();
  54. }
  55. if($root=='/'){
  56. $root='';
  57. }
  58. $path=$root.$path;
  59. $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
  60. $result=$query->execute(array(md5($path)))->fetchRow();
  61. if(is_array($result)){
  62. return $result;
  63. }else{
  64. OC_Log::write('files','get(): file not found in cache ('.$path.')',OC_Log::DEBUG);
  65. return false;
  66. }
  67. }
  68. /**
  69. * put filesystem info in the cache
  70. * @param string $path
  71. * @param array data
  72. * @param string root (optional)
  73. *
  74. * $data is an assiciative array in the same format as returned by get
  75. */
  76. public static function put($path,$data,$root=''){
  77. if(!$root){
  78. $root=OC_Filesystem::getRoot();
  79. }
  80. if($root=='/'){
  81. $root='';
  82. }
  83. $path=$root.$path;
  84. if($path=='/'){
  85. $parent=-1;
  86. }else{
  87. $parent=self::getFileId(dirname($path));
  88. }
  89. $id=self::getFileId($path);
  90. if($id!=-1){
  91. self::update($id,$data);
  92. return;
  93. }
  94. if(isset(self::$savedData[$path])){
  95. $data=array_merge($data,self::$savedData[$path]);
  96. unset(self::$savedData[$path]);
  97. }
  98. if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it
  99. self::$savedData[$path]=$data;
  100. return;
  101. }
  102. if(!isset($data['encrypted'])){
  103. $data['encrypted']=false;
  104. }
  105. if(!isset($data['versioned'])){
  106. $data['versioned']=false;
  107. }
  108. $mimePart=dirname($data['mimetype']);
  109. $user=OC_User::getUser();
  110. $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, path_hash, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)');
  111. $result=$query->execute(array($parent,basename($path),$path,md5($path),$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
  112. if(OC_DB::isError($result)){
  113. OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR);
  114. }
  115. }
  116. /**
  117. * update filesystem info of a file
  118. * @param int $id
  119. * @param array $data
  120. */
  121. private static function update($id,$data){
  122. $arguments=array();
  123. $queryParts=array();
  124. foreach(array('size','mtime','ctime','mimetype','encrypted','versioned','writable') as $attribute){
  125. if(isset($data[$attribute])){
  126. $arguments[]=$data[$attribute];
  127. $queryParts[]=$attribute.'=?';
  128. }
  129. }
  130. if(isset($data['mimetype'])){
  131. $arguments[]=dirname($data['mimetype']);
  132. $queryParts[]='mimepart=?';
  133. }
  134. $arguments[]=$id;
  135. $sql = 'UPDATE *PREFIX*fscache SET '.implode(' , ',$queryParts).' WHERE id=?';
  136. $query=OC_DB::prepare($sql);
  137. $result=$query->execute($arguments);
  138. if(OC_DB::isError($result)){
  139. OC_Log::write('files','error while updating file('.$path.') in cache',OC_Log::ERROR);
  140. }
  141. }
  142. /**
  143. * register a file move in the cache
  144. * @param string oldPath
  145. * @param string newPath
  146. * @param string root (optional)
  147. */
  148. public static function move($oldPath,$newPath,$root=''){
  149. if(!$root){
  150. $root=OC_Filesystem::getRoot();
  151. }
  152. if($root=='/'){
  153. $root='';
  154. }
  155. $oldPath=$root.$oldPath;
  156. $newPath=$root.$newPath;
  157. $newParent=self::getParentId($newPath);
  158. $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=?, path_hash=? WHERE path_hash=?');
  159. $query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath)));
  160. }
  161. /**
  162. * delete info from the cache
  163. * @param string/int $file
  164. * @param string root (optional)
  165. */
  166. public static function delete($file,$root=''){
  167. if(!is_numeric($file)){
  168. if(!$root){
  169. $root=OC_Filesystem::getRoot();
  170. }
  171. if($root=='/'){
  172. $root='';
  173. }
  174. $path=$root.$file;
  175. self::delete(self::getFileId($path));
  176. }elseif($file!=-1){
  177. $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE parent=?');
  178. $result=$query->execute(array($file));
  179. while($child=$result->fetchRow()){
  180. self::delete(intval($child['id']));
  181. }
  182. $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE id=?');
  183. $query->execute(array($file));
  184. }
  185. }
  186. /**
  187. * return array of filenames matching the querty
  188. * @param string $query
  189. * @param boolean $returnData
  190. * @param string root (optional)
  191. * @return array of filepaths
  192. */
  193. public static function search($search,$returnData=false,$root=''){
  194. if(!$root){
  195. $root=OC_Filesystem::getRoot();
  196. }
  197. if($root=='/'){
  198. $root='';
  199. }
  200. $rootLen=strlen($root);
  201. if(!$returnData){
  202. $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE name LIKE ? AND user=?');
  203. }else{
  204. $query=OC_DB::prepare('SELECT * FROM *PREFIX*fscache WHERE name LIKE ? AND user=?');
  205. }
  206. $result=$query->execute(array("%$search%",OC_User::getUser()));
  207. $names=array();
  208. while($row=$result->fetchRow()){
  209. if(!$returnData){
  210. $names[]=substr($row['path'],$rootLen);
  211. }else{
  212. $row['path']=substr($row['path'],$rootLen);
  213. $names[]=$row;
  214. }
  215. }
  216. return $names;
  217. }
  218. /**
  219. * get all files and folders in a folder
  220. * @param string path
  221. * @param string root (optional)
  222. * @return array
  223. *
  224. * returns an array of assiciative arrays with the following keys:
  225. * - name
  226. * - size
  227. * - mtime
  228. * - ctime
  229. * - mimetype
  230. * - encrypted
  231. * - versioned
  232. */
  233. public static function getFolderContent($path,$root='',$mimetype_filter=''){
  234. if(self::isUpdated($path,$root)){
  235. self::updateFolder($path,$root);
  236. }
  237. if(!$root){
  238. $root=OC_Filesystem::getRoot();
  239. }
  240. if($root=='/'){
  241. $root='';
  242. }
  243. $path=$root.$path;
  244. $parent=self::getFileId($path);
  245. $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=? AND (mimetype LIKE ? OR mimetype = ?)');
  246. $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll();
  247. if(is_array($result)){
  248. return $result;
  249. }else{
  250. OC_Log::write('files','getFolderContent(): file not found in cache ('.$path.')',OC_Log::DEBUG);
  251. return false;
  252. }
  253. }
  254. /**
  255. * check if a file or folder is in the cache
  256. * @param string $path
  257. * @param string root (optional)
  258. * @return bool
  259. */
  260. public static function inCache($path,$root=''){
  261. if(!$root){
  262. $root=OC_Filesystem::getRoot();
  263. }
  264. if($root=='/'){
  265. $root='';
  266. }
  267. $path=$root.$path;
  268. return self::getFileId($path)!=-1;
  269. }
  270. /**
  271. * get the file id as used in the cache
  272. * unlike the public getId, full paths are used here (/usename/files/foo instead of /foo)
  273. * @param string $path
  274. * @return int
  275. */
  276. private static function getFileId($path){
  277. $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path_hash=?');
  278. if(OC_DB::isError($query)){
  279. OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
  280. return -1;
  281. }
  282. $result=$query->execute(array(md5($path)));
  283. if(OC_DB::isError($result)){
  284. OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
  285. return -1;
  286. }
  287. $result=$result->fetchRow();
  288. if(is_array($result)){
  289. return $result['id'];
  290. }else{
  291. OC_Log::write('files','getFileId(): file not found in cache ('.$path.')',OC_Log::DEBUG);
  292. return -1;
  293. }
  294. }
  295. /**
  296. * get the file id as used in the cache
  297. * @param string path
  298. * @param string root (optional)
  299. * @return int
  300. */
  301. public static function getId($path,$root=''){
  302. if(!$root){
  303. $root=OC_Filesystem::getRoot();
  304. }
  305. if($root=='/'){
  306. $root='';
  307. }
  308. $path=$root.$path;
  309. return self::getFileId($path);
  310. }
  311. /**
  312. * get the file path from the id, relative to the home folder of the user
  313. * @param int id
  314. * @param string user (optional)
  315. * @return string
  316. */
  317. public static function getPath($id,$user=''){
  318. if(!$user){
  319. $user=OC_User::getUser();
  320. }
  321. $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id=? AND user=?');
  322. $result=$query->execute(array($id,$user));
  323. $row=$result->fetchRow();
  324. $path=$row['path'];
  325. $root='/'.$user.'/files';
  326. if(substr($path,0,strlen($root))!=$root){
  327. return false;
  328. }
  329. return substr($path,strlen($root));
  330. }
  331. /**
  332. * get the file id of the parent folder, taking into account '/' has no parent
  333. * @param string $path
  334. * @return int
  335. */
  336. private static function getParentId($path){
  337. if($path=='/'){
  338. return -1;
  339. }else{
  340. return self::getFileId(dirname($path));
  341. }
  342. }
  343. /**
  344. * called when changes are made to files
  345. * @param array $params
  346. * @param string root (optional)
  347. */
  348. public static function fileSystemWatcherWrite($params,$root=''){
  349. if(!$root){
  350. $view=OC_Filesystem::getView();
  351. }else{
  352. $view=new OC_FilesystemView(($root=='/')?'':$root);
  353. }
  354. $path=$params['path'];
  355. $fullPath=$view->getRoot().$path;
  356. $mimetype=$view->getMimeType($path);
  357. $dir=$view->is_dir($path.'/');
  358. //dont use self::get here, we don't want inifinte loops when a file has changed
  359. $cachedSize=self::getCachedSize($path,$root);
  360. $size=0;
  361. if($dir){
  362. if(self::inCache($path,$root)){
  363. $parent=self::getFileId($fullPath);
  364. $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE parent=?');
  365. $result=$query->execute(array($parent));
  366. while($row=$result->fetchRow()){
  367. $size+=$row['size'];
  368. }
  369. $mtime=$view->filemtime($path);
  370. $ctime=$view->filectime($path);
  371. $writable=$view->is_writable($path);
  372. self::put($path,array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable));
  373. }else{
  374. $count=0;
  375. self::scan($path,null,$count,$root);
  376. }
  377. }else{
  378. $size=self::scanFile($path,$root);
  379. }
  380. self::increaseSize(dirname($fullPath),$size-$cachedSize);
  381. }
  382. public static function getCached($path,$root=''){
  383. if(!$root){
  384. $root=OC_Filesystem::getRoot();
  385. }else{
  386. if($root=='/'){
  387. $root='';
  388. }
  389. }
  390. $path=$root.$path;
  391. $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
  392. $result=$query->execute(array(md5($path)))->fetchRow();
  393. if(is_array($result)){
  394. if(isset(self::$savedData[$path])){
  395. $result=array_merge($result,self::$savedData[$path]);
  396. }
  397. return $result;
  398. }else{
  399. OC_Log::write('files','getChached(): file not found in cache ('.$path.')',OC_Log::DEBUG);
  400. if(isset(self::$savedData[$path])){
  401. return self::$savedData[$path];
  402. }else{
  403. return array();
  404. }
  405. }
  406. }
  407. private static function getCachedSize($path,$root){
  408. if(!$root){
  409. $root=OC_Filesystem::getRoot();
  410. }else{
  411. if($root=='/'){
  412. $root='';
  413. }
  414. }
  415. $path=$root.$path;
  416. $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path_hash=?');
  417. $result=$query->execute(array(md5($path)));
  418. if($row=$result->fetchRow()){
  419. return $row['size'];
  420. }else{//file not in cache
  421. return 0;
  422. }
  423. }
  424. /**
  425. * called when files are deleted
  426. * @param array $params
  427. * @param string root (optional)
  428. */
  429. public static function fileSystemWatcherDelete($params,$root=''){
  430. if(!$root){
  431. $root=OC_Filesystem::getRoot();
  432. }
  433. if($root=='/'){
  434. $root='';
  435. }
  436. $path=$params['path'];
  437. $fullPath=$root.$path;
  438. if(self::getFileId($fullPath)==-1){
  439. return;
  440. }
  441. $size=self::getCachedSize($path,$root);
  442. self::increaseSize(dirname($fullPath),-$size);
  443. self::delete($path);
  444. }
  445. /**
  446. * called when files are deleted
  447. * @param array $params
  448. * @param string root (optional)
  449. */
  450. public static function fileSystemWatcherRename($params,$root=''){
  451. if(!$root){
  452. $root=OC_Filesystem::getRoot();
  453. }
  454. if($root=='/'){
  455. $root='';
  456. }
  457. $oldPath=$params['oldpath'];
  458. $newPath=$params['newpath'];
  459. $fullOldPath=$root.$oldPath;
  460. $fullNewPath=$root.$newPath;
  461. if(($id=self::getFileId($fullOldPath))!=-1){
  462. $oldSize=self::getCachedSize($oldPath,$root);
  463. }else{
  464. return;
  465. }
  466. $size=OC_Filesystem::filesize($newPath);
  467. self::increaseSize(dirname($fullOldPath),-$oldSize);
  468. self::increaseSize(dirname($fullNewPath),$oldSize);
  469. self::move($oldPath,$newPath);
  470. }
  471. /**
  472. * adjust the size of the parent folders
  473. * @param string $path
  474. * @param int $sizeDiff
  475. */
  476. private static function increaseSize($path,$sizeDiff){
  477. if($sizeDiff==0) return;
  478. while(($id=self::getFileId($path))!=-1){//walk up the filetree increasing the size of all parent folders
  479. $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET size=size+? WHERE id=?');
  480. $query->execute(array($sizeDiff,$id));
  481. $path=dirname($path);
  482. }
  483. }
  484. /**
  485. * recursively scan the filesystem and fill the cache
  486. * @param string $path
  487. * @param OC_EventSource $enventSource (optional)
  488. * @param int count (optional)
  489. * @param string root (optionak)
  490. */
  491. public static function scan($path,$eventSource=false,&$count=0,$root=''){
  492. if($eventSource){
  493. $eventSource->send('scanning',array('file'=>$path,'count'=>$count));
  494. }
  495. $lastSend=$count;
  496. if(!$root){
  497. $view=OC_Filesystem::getView();
  498. }else{
  499. $view=new OC_FilesystemView(($root=='/')?'':$root);
  500. }
  501. self::scanFile($path,$root);
  502. $dh=$view->opendir($path.'/');
  503. $totalSize=0;
  504. if($dh){
  505. while (($filename = readdir($dh)) !== false) {
  506. if($filename != '.' and $filename != '..'){
  507. $file=$path.'/'.$filename;
  508. if($view->is_dir($file.'/')){
  509. self::scan($file,$eventSource,$count,$root);
  510. }else{
  511. $totalSize+=self::scanFile($file,$root);
  512. $count++;
  513. if($count>$lastSend+25 and $eventSource){
  514. $lastSend=$count;
  515. $eventSource->send('scanning',array('file'=>$path,'count'=>$count));
  516. }
  517. }
  518. }
  519. }
  520. }
  521. self::cleanFolder($path,$root);
  522. self::increaseSize($view->getRoot().$path,$totalSize);
  523. }
  524. /**
  525. * scan a single file
  526. * @param string path
  527. * @param string root (optional)
  528. * @return int size of the scanned file
  529. */
  530. public static function scanFile($path,$root=''){
  531. if(!$root){
  532. $view=OC_Filesystem::getView();
  533. }else{
  534. $view=new OC_FilesystemView(($root=='/')?'':$root);
  535. }
  536. if(!$view->is_readable($path)) return; //cant read, nothing we can do
  537. clearstatcache();
  538. $stat=$view->stat($path);
  539. $mimetype=$view->getMimeType($path);
  540. $writable=$view->is_writable($path);
  541. $stat['mimetype']=$mimetype;
  542. $stat['writable']=$writable;
  543. if($path=='/'){
  544. $path='';
  545. }
  546. self::put($path,$stat,$root);
  547. return $stat['size'];
  548. }
  549. /**
  550. * find files by mimetype
  551. * @param string $part1
  552. * @param string $part2 (optional)
  553. * @param string root (optional)
  554. * @return array of file paths
  555. *
  556. * $part1 and $part2 together form the complete mimetype.
  557. * e.g. searchByMime('text','plain')
  558. *
  559. * seccond mimetype part can be ommited
  560. * e.g. searchByMime('audio')
  561. */
  562. public static function searchByMime($part1,$part2=null,$root=null){
  563. if(!$root){
  564. $root=OC_Filesystem::getRoot();
  565. }elseif($root=='/'){
  566. $root='';
  567. }
  568. $rootLen=strlen($root);
  569. $root .= '%';
  570. $user=OC_User::getUser();
  571. if(!$part2){
  572. $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimepart=? AND user=? AND path LIKE ?');
  573. $result=$query->execute(array($part1,$user, $root));
  574. }else{
  575. $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimetype=? AND user=? AND path LIKE ? ');
  576. $result=$query->execute(array($part1.'/'.$part2,$user, $root));
  577. }
  578. $names=array();
  579. while($row=$result->fetchRow()){
  580. $names[]=substr($row['path'],$rootLen);
  581. }
  582. return $names;
  583. }
  584. /**
  585. * check if a file or folder is updated outside owncloud
  586. * @param string path
  587. * @param string root (optional)
  588. * @return bool
  589. */
  590. public static function isUpdated($path,$root=''){
  591. if(!$root){
  592. $root=OC_Filesystem::getRoot();
  593. $view=OC_Filesystem::getView();
  594. }else{
  595. if($root=='/'){
  596. $root='';
  597. }
  598. $view=new OC_FilesystemView($root);
  599. }
  600. if(!$view->file_exists($path)){
  601. return false;
  602. }
  603. $mtime=$view->filemtime($path);
  604. $isDir=$view->is_dir($path);
  605. $path=$root.$path;
  606. $query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path_hash=?');
  607. $result=$query->execute(array(md5($path)));
  608. if($row=$result->fetchRow()){
  609. $cachedMTime=$row['mtime'];
  610. return ($mtime>$cachedMTime);
  611. }else{//file not in cache, so it has to be updated
  612. return !($isDir);//new folders are handeled sperate
  613. }
  614. }
  615. /**
  616. * update the cache according to changes in the folder
  617. * @param string path
  618. * @param string root (optional)
  619. */
  620. private static function updateFolder($path,$root=''){
  621. if(!$root){
  622. $view=OC_Filesystem::getView();
  623. }else{
  624. $view=new OC_FilesystemView(($root=='/')?'':$root);
  625. }
  626. $dh=$view->opendir($path);
  627. if($dh){//check for changed/new files
  628. while (($filename = readdir($dh)) !== false) {
  629. if($filename != '.' and $filename != '..'){
  630. $file=$path.'/'.$filename;
  631. if(self::isUpdated($file,$root)){
  632. if(!$root){//filesystem hooks are only valid for the default root
  633. OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$file));
  634. }else{
  635. self::fileSystemWatcherWrite(array('path'=>$file),$root);
  636. }
  637. }
  638. }
  639. }
  640. }
  641. self::cleanFolder($path,$root);
  642. //update the folder last, so we can calculate the size correctly
  643. if(!$root){//filesystem hooks are only valid for the default root
  644. OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path));
  645. }else{
  646. self::fileSystemWatcherWrite(array('path'=>$path),$root);
  647. }
  648. }
  649. /**
  650. * delete non existing files from the cache
  651. */
  652. private static function cleanFolder($path,$root=''){
  653. if(!$root){
  654. $view=OC_Filesystem::getView();
  655. }else{
  656. $view=new OC_FilesystemView(($root=='/')?'':$root);
  657. }
  658. //check for removed files, not using getFolderContent to prevent loops
  659. $parent=self::getFileId($view->getRoot().$path);
  660. $query=OC_DB::prepare('SELECT name FROM *PREFIX*fscache WHERE parent=?');
  661. $result=$query->execute(array($parent));
  662. while($row=$result->fetchRow()){
  663. $file=$path.'/'.$row['name'];
  664. if(!$view->file_exists($file)){
  665. if(!$root){//filesystem hooks are only valid for the default root
  666. OC_Hook::emit('OC_Filesystem','post_delete',array('path'=>$file));
  667. }else{
  668. self::fileSystemWatcherDelete(array('path'=>$file),$root);
  669. }
  670. }
  671. }
  672. }
  673. /**
  674. * clean old pre-path_hash entries
  675. */
  676. public static function clean(){
  677. $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE LENGTH(path_hash)<30');
  678. $query->execute();
  679. }
  680. }
  681. //watch for changes and try to keep the cache up to date
  682. OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache','fileSystemWatcherWrite');
  683. OC_Hook::connect('OC_Filesystem','post_delete','OC_FileCache','fileSystemWatcherDelete');
  684. OC_Hook::connect('OC_Filesystem','post_rename','OC_FileCache','fileSystemWatcherRename');