PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/ecrire/inc/flock.php

https://github.com/denisbz/SPIP-1
PHP | 411 lines | 248 code | 46 blank | 117 comment | 73 complexity | 3e5af07ac97851198b373b0dd4da4a98 MD5 | raw file
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2011 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. if (!defined('_ECRIRE_INC_VERSION')) return;
  12. // ajouter define('_CREER_DIR_PLAT', true); dans mes_options pour restaurer
  13. // le fonctionnement des faux repertoires en .plat
  14. define('_CREER_DIR_PLAT', false);
  15. if (!defined('_TEST_FILE_EXISTS')) define('_TEST_FILE_EXISTS', preg_match(',(online|free)[.]fr$,', $_ENV["HTTP_HOST"]));
  16. #define('_SPIP_LOCK_MODE',0); // ne pas utiliser de lock (deconseille)
  17. #define('_SPIP_LOCK_MODE',1); // utiliser le flock php
  18. #define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip
  19. if (_SPIP_LOCK_MODE==2)
  20. include_spip('inc/nfslock');
  21. $GLOBALS['liste_verrous'] = array();
  22. // http://doc.spip.org/@spip_fopen_lock
  23. function spip_fopen_lock($fichier,$mode,$verrou){
  24. if (_SPIP_LOCK_MODE==1){
  25. if ($fl = @fopen($fichier,$mode))
  26. // verrou
  27. @flock($fl, $verrou);
  28. return $fl;
  29. }
  30. elseif(_SPIP_LOCK_MODE==2) {
  31. if (($verrou = spip_nfslock($fichier)) && ($fl = @fopen($fichier,$mode))){
  32. $GLOBALS['liste_verrous'][$fl] = array($fichier,$verrou);
  33. return $fl;
  34. }
  35. else return false;
  36. }
  37. return @fopen($fichier,$mode);
  38. }
  39. // http://doc.spip.org/@spip_fclose_unlock
  40. function spip_fclose_unlock($handle){
  41. if (_SPIP_LOCK_MODE==1){
  42. @flock($handle, LOCK_UN);
  43. }
  44. elseif(_SPIP_LOCK_MODE==2) {
  45. spip_nfsunlock(reset($GLOBALS['liste_verrous'][$handle]),end($GLOBALS['liste_verrous'][$handle]));
  46. unset($GLOBALS['liste_verrous'][$handle]);
  47. }
  48. return @fclose($handle);
  49. }
  50. // http://doc.spip.org/@spip_file_get_contents
  51. function spip_file_get_contents ($fichier) {
  52. if (substr($fichier, -3) != '.gz') {
  53. if (function_exists('file_get_contents')
  54. AND (
  55. // quand on est sous window on ne sait pas si file_get_contents marche
  56. // on essaye : si ca retourne du contenu alors c'est bon
  57. // sinon on fait un file() pour avoir le coeur net
  58. ($contenu = @file_get_contents ($fichier))
  59. OR _OS_SERVEUR != 'windows')
  60. )
  61. return $contenu;
  62. else
  63. $contenu = @file($fichier);
  64. } else
  65. $contenu = @gzfile($fichier);
  66. return is_array($contenu)?join('', $contenu):(string)$contenu;
  67. }
  68. // options = array(
  69. // 'phpcheck' => 'oui' # verifier qu'on a bien du php
  70. // dezippe automatiquement les fichiers .gz
  71. // http://doc.spip.org/@lire_fichier
  72. function lire_fichier ($fichier, &$contenu, $options=false) {
  73. $contenu = '';
  74. // inutile car si le fichier n'existe pas, le lock va renvoyer false juste apres
  75. // economisons donc les acces disque, sauf chez free qui rale pour un rien
  76. if (_TEST_FILE_EXISTS AND !@file_exists($fichier))
  77. return false;
  78. #spip_timer('lire_fichier');
  79. // pas de @ sur spip_fopen_lock qui est silencieux de toute facon
  80. if ($fl = spip_fopen_lock($fichier, 'r', LOCK_SH)) {
  81. // lire le fichier avant tout
  82. $contenu = spip_file_get_contents($fichier);
  83. // le fichier a-t-il ete supprime par le locker ?
  84. // on ne verifie que si la tentative de lecture a echoue
  85. // pour discriminer un contenu vide d'un fichier absent
  86. // et eviter un acces disque
  87. if (!$contenu AND !@file_exists($fichier)) {
  88. spip_fclose_unlock($fl);
  89. return false;
  90. }
  91. // liberer le verrou
  92. spip_fclose_unlock($fl);
  93. // Verifications
  94. $ok = true;
  95. if ($options['phpcheck'] == 'oui')
  96. $ok &= (preg_match(",[?]>\n?$,", $contenu));
  97. #spip_log("$fread $fichier ".spip_timer('lire_fichier'));
  98. if (!$ok)
  99. spip_log("echec lecture $fichier");
  100. return $ok;
  101. }
  102. return false;
  103. }
  104. //
  105. // Ecrire un fichier de maniere un peu sure
  106. // $ecrire_quand_meme ne sert plus mais est conservee dans l'appel pour compatibilite
  107. //
  108. // zippe les fichiers .gz
  109. // http://doc.spip.org/@ecrire_fichier
  110. function ecrire_fichier ($fichier, $contenu, $ecrire_quand_meme = false, $truncate=true) {
  111. #spip_timer('ecrire_fichier');
  112. // verrouiller le fichier destination
  113. if ($fp = spip_fopen_lock($fichier, 'a',LOCK_EX)) {
  114. // ecrire les donnees, compressees le cas echeant
  115. // (on ouvre un nouveau pointeur sur le fichier, ce qui a l'avantage
  116. // de le recreer si le locker qui nous precede l'avait supprime...)
  117. if (substr($fichier, -3) == '.gz')
  118. $contenu = gzencode($contenu);
  119. // si c'est une ecriture avec troncation , on fait plutot une ecriture complete a cote suivie unlink+rename
  120. // pour etre sur d'avoir une operation atomique
  121. // y compris en NFS : http://www.ietf.org/rfc/rfc1094.txt
  122. // sauf sous wintruc ou ca ne marche pas
  123. $ok = false;
  124. if ($truncate AND _OS_SERVEUR != 'windows'){
  125. if (!function_exists('creer_uniqid'))
  126. include_spip('inc/acces');
  127. $id = creer_uniqid();
  128. // on ouvre un pointeur sur un fichier temporaire en ecriture +raz
  129. if ($fp2 = spip_fopen_lock("$fichier.$id", 'w',LOCK_EX)) {
  130. $s = @fputs($fp2, $contenu, $a = strlen($contenu));
  131. $ok = ($s == $a);
  132. spip_fclose_unlock($fp2);
  133. spip_fclose_unlock($fp);
  134. // unlink direct et pas spip_unlink car on avait deja le verrou
  135. @unlink($fichier);
  136. // le rename aussitot, atomique quand on est pas sous windows
  137. // au pire on arrive en second en cas de concourance, et le rename echoue
  138. // --> on a la version de l'autre process qui doit etre identique
  139. @rename("$fichier.$id",$fichier);
  140. // precaution en cas d'echec du rename
  141. if (!_TEST_FILE_EXISTS OR @file_exists("$fichier.$id"))
  142. @unlink("$fichier.$id");
  143. if ($ok)
  144. $ok = file_exists($fichier);
  145. }
  146. else
  147. // echec mais penser a fermer ..
  148. spip_fclose_unlock($fp);
  149. }
  150. // sinon ou si methode precedente a echoueee
  151. // on se rabat sur la methode ancienne
  152. if (!$ok){
  153. // ici on est en ajout ou sous windows, cas desespere
  154. if ($truncate)
  155. @ftruncate($fp,0);
  156. $s = @fputs($fp, $contenu, $a = strlen($contenu));
  157. $ok = ($s == $a);
  158. spip_fclose_unlock($fp);
  159. }
  160. // liberer le verrou et fermer le fichier
  161. @chmod($fichier, _SPIP_CHMOD & 0666);
  162. if ($ok) return $ok;
  163. }
  164. include_spip('inc/autoriser');
  165. if (autoriser('chargerftp'))
  166. raler_fichier($fichier);
  167. spip_unlink($fichier);
  168. return false;
  169. }
  170. /**
  171. * Ecrire un contenu dans un fichier encapsule en php pour en empecher l'acces en l'absence
  172. * de htaccess
  173. * @param string $fichier
  174. * @param <type> $contenu
  175. * @param <type> $ecrire_quand_meme
  176. * @param <type> $truncate
  177. */
  178. function ecrire_fichier_securise ($fichier, $contenu, $ecrire_quand_meme = false, $truncate=true) {
  179. if (substr($fichier,-4) !== '.php')
  180. spip_log('Erreur de programmation: '.$fichier.' doit finir par .php');
  181. $contenu = "<"."?php die ('Acces interdit'); ?".">\n" . $contenu;
  182. return ecrire_fichier($fichier, $contenu, $ecrire_quand_meme, $truncate);
  183. }
  184. /**
  185. * Lire un fichier encapsule en php
  186. * @param <type> $fichier
  187. * @param <type> $contenu
  188. * @param <type> $options
  189. */
  190. function lire_fichier_securise ($fichier, &$contenu, $options=false) {
  191. if ($res = lire_fichier($fichier,$contenu,$options)){
  192. $contenu = substr($contenu,strlen("<"."?php die ('Acces interdit'); ?".">\n"));
  193. }
  194. return $res;
  195. }
  196. // http://doc.spip.org/@raler_fichier
  197. function raler_fichier($fichier)
  198. {
  199. include_spip('inc/minipres');
  200. $dir = dirname($fichier);
  201. http_status(401);
  202. echo minipres(_T('texte_inc_meta_2'), "<h4 style='color: red'>"
  203. . _T('texte_inc_meta_1', array('fichier' => $fichier))
  204. . " <a href='"
  205. . generer_url_ecrire('install', "etape=chmod&test_dir=$dir")
  206. . "'>"
  207. . _T('texte_inc_meta_2')
  208. . "</a> "
  209. . _T('texte_inc_meta_3',
  210. array('repertoire' => joli_repertoire($dir)))
  211. . "</h4>\n");
  212. exit;
  213. }
  214. //
  215. // Retourne Vrai si son premier argument a ete cree il y a moins de N secondes
  216. //
  217. // http://doc.spip.org/@jeune_fichier
  218. function jeune_fichier($fichier, $n)
  219. {
  220. if (!file_exists($fichier)) return false;
  221. if (!$c = @filemtime($fichier)) return false;
  222. return (time()-$n <= $c);
  223. }
  224. //
  225. // Supprimer le fichier de maniere sympa (flock)
  226. //
  227. // http://doc.spip.org/@supprimer_fichier
  228. function supprimer_fichier($fichier, $lock=true) {
  229. if (!@file_exists($fichier))
  230. return true;
  231. if ($lock) {
  232. // verrouiller le fichier destination
  233. if (!$fp = spip_fopen_lock($fichier, 'a', LOCK_EX))
  234. return false;
  235. // liberer le verrou
  236. spip_fclose_unlock($fp);
  237. }
  238. // supprimer
  239. return @unlink($fichier);
  240. }
  241. // Supprimer brutalement, si le fichier existe
  242. // http://doc.spip.org/@spip_unlink
  243. function spip_unlink($f) {
  244. if (!is_dir($f))
  245. supprimer_fichier($f,false);
  246. else {
  247. @unlink("$f/.ok");
  248. @rmdir($f);
  249. }
  250. }
  251. //
  252. // Retourne $base/${subdir}/ si le sous-repertoire peut etre cree,
  253. // $base/${subdir}_ sinon ; $nobase signale qu'on ne veut pas de $base/
  254. // On peut aussi ne donner qu'un seul argument,
  255. // subdir valant alors ce qui suit le dernier / dans $base
  256. //
  257. // http://doc.spip.org/@sous_repertoire
  258. function sous_repertoire($base, $subdir='', $nobase = false, $tantpis=false) {
  259. static $dirs = array();
  260. $base = str_replace("//", "/", $base);
  261. # suppr le dernier caractere si c'est un / ou un _
  262. $base = rtrim($base, '/_');
  263. if (!strlen($subdir)) {
  264. $n = strrpos($base, "/");
  265. if ($n === false) return $nobase ? '' : ($base .'/');
  266. $subdir = substr($base, $n+1);
  267. $base = substr($base, 0, $n+1);
  268. } else {
  269. $base .= '/';
  270. $subdir = str_replace("/", "", $subdir);
  271. }
  272. $baseaff = $nobase ? '' : $base;
  273. if (isset($dirs[$base.$subdir]))
  274. return $baseaff.$dirs[$base.$subdir];
  275. if (_CREER_DIR_PLAT AND @file_exists("$base${subdir}.plat"))
  276. return $baseaff.($dirs[$base.$subdir] = "${subdir}_");
  277. $path = $base.$subdir; # $path = 'IMG/distant/pdf' ou 'IMG/distant_pdf'
  278. if (file_exists("$path/.ok"))
  279. return $baseaff.($dirs[$base.$subdir] = "$subdir/");
  280. @mkdir($path, _SPIP_CHMOD);
  281. @chmod($path, _SPIP_CHMOD);
  282. $ok = false;
  283. if ($test = @fopen("$path/dir_test.php", "w")) {
  284. @fputs($test, '<'.'?php $ok = true; ?'.'>');
  285. @fclose($test);
  286. @include("$path/dir_test.php");
  287. spip_unlink("$path/dir_test.php");
  288. }
  289. if ($ok) {
  290. @touch ("$path/.ok");
  291. spip_log("creation $base$subdir/");
  292. return $baseaff.($dirs[$base.$subdir] = "$subdir/");
  293. }
  294. // en cas d'echec c'est peut etre tout simplement que le disque est plein :
  295. // l'inode du fichier dir_test existe, mais impossible d'y mettre du contenu
  296. // => sauf besoin express (define dans mes_options), ne pas creer le .plat
  297. if (_CREER_DIR_PLAT
  298. AND $f = @fopen("$base${subdir}.plat", "w"))
  299. fclose($f);
  300. else {
  301. spip_log("echec creation $base${subdir}");
  302. if ($tantpis) return '';
  303. if (!_DIR_RESTREINT)
  304. $base = preg_replace(',^' . _DIR_RACINE .',', '',$base);
  305. if ($test) $base .= $subdir;
  306. raler_fichier($base . '/.ok');
  307. }
  308. spip_log("faux sous-repertoire $base${subdir}");
  309. return $baseaff.($dirs[$base.$subdirs] = "${subdir}_");
  310. }
  311. //
  312. // Cette fonction parcourt recursivement le repertoire $dir, et renvoie les
  313. // fichiers dont le chemin verifie le pattern (preg) donne en argument.
  314. // En cas d'echec retourne un array() vide
  315. //
  316. // Usage: array preg_files('ecrire/data/', '[.]lock$');
  317. //
  318. // Attention, afin de conserver la compatibilite avec les repertoires '.plat'
  319. // si $dir = 'rep/sous_rep_' au lieu de 'rep/sous_rep/' on scanne 'rep/' et on
  320. // applique un pattern '^rep/sous_rep_'
  321. // si $recurs vaut false, la fonction ne descend pas dans les sus repertoires
  322. //
  323. // http://doc.spip.org/@preg_files
  324. function preg_files($dir, $pattern=-1 /* AUTO */, $maxfiles = 10000, $recurs=array()) {
  325. $nbfiles = 0;
  326. if ($pattern == -1)
  327. $pattern = "^$dir";
  328. $fichiers = array();
  329. // revenir au repertoire racine si on a recu dossier/truc
  330. // pour regarder dossier/truc/ ne pas oublier le / final
  331. $dir = preg_replace(',/[^/]*$,', '', $dir);
  332. if ($dir == '') $dir = '.';
  333. if (@is_dir($dir) AND is_readable($dir) AND $d = @opendir($dir)) {
  334. while (($f = readdir($d)) !== false && ($nbfiles<$maxfiles)) {
  335. if ($f[0] != '.' # ignorer . .. .svn etc
  336. AND $f != 'CVS'
  337. AND $f != 'remove.txt'
  338. AND is_readable($f = "$dir/$f")) {
  339. if (is_file($f)) {
  340. if (preg_match(";$pattern;iS", $f))
  341. {
  342. $fichiers[] = $f;
  343. $nbfiles++;
  344. }
  345. }
  346. else if (is_dir($f) AND is_array($recurs)){
  347. $rp = @realpath($f);
  348. if (!is_string($rp) OR !strlen($rp)) $rp=$f; # realpath n'est peut etre pas autorise
  349. if (!isset($recurs[$rp])) {
  350. $recurs[$rp] = true;
  351. $beginning = $fichiers;
  352. $end = preg_files("$f/", $pattern,
  353. $maxfiles-$nbfiles, $recurs);
  354. $fichiers = array_merge((array)$beginning, (array)$end);
  355. $nbfiles = count($fichiers);
  356. }
  357. }
  358. }
  359. }
  360. closedir($d);
  361. }
  362. sort($fichiers);
  363. return $fichiers;
  364. }
  365. ?>