PageRenderTime 44ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/private/tempmanager.php

https://gitlab.com/wuhang2003/core
PHP | 277 lines | 229 code | 6 blank | 42 comment | 1 complexity | 48aa83a1e6736bda1deab8f5064f3b67 MD5 | raw file
  1. <?php
  2. /**
  3. * @author Lars <winnetou+github@catolic.de>
  4. * @author Lukas Reschke <lukas@owncloud.com>
  5. * @author Martin Mattel <martin.mattel@diemattels.at>
  6. * @author Morris Jobke <hey@morrisjobke.de>
  7. * @author Olivier Paroz <github@oparoz.com>
  8. * @author Robin Appelman <icewind@owncloud.com>
  9. * @author Robin McCorkell <robin@mccorkell.me.uk>
  10. *
  11. * @copyright Copyright (c) 2016, ownCloud, Inc.
  12. * @license AGPL-3.0
  13. *
  14. * This code is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License, version 3,
  16. * as published by the Free Software Foundation.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License, version 3,
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>
  25. *
  26. */
  27. namespace OC;
  28. use OCP\ILogger;
  29. use OCP\IConfig;
  30. use OCP\ITempManager;
  31. class TempManager implements ITempManager {
  32. /** @var string[] Current temporary files and folders, used for cleanup */
  33. protected $current = [];
  34. /** @var string i.e. /tmp on linux systems */
  35. protected $tmpBaseDir;
  36. /** @var ILogger */
  37. protected $log;
  38. /** @var IConfig */
  39. protected $config;
  40. /** Prefix */
  41. const TMP_PREFIX = 'oc_tmp_';
  42. /**
  43. * @param \OCP\ILogger $logger
  44. * @param \OCP\IConfig $config
  45. */
  46. public function __construct(ILogger $logger, IConfig $config) {
  47. $this->log = $logger;
  48. $this->config = $config;
  49. $this->tmpBaseDir = $this->getTempBaseDir();
  50. }
  51. /**
  52. * Builds the filename with suffix and removes potential dangerous characters
  53. * such as directory separators.
  54. *
  55. * @param string $absolutePath Absolute path to the file / folder
  56. * @param string $postFix Postfix appended to the temporary file name, may be user controlled
  57. * @return string
  58. */
  59. private function buildFileNameWithSuffix($absolutePath, $postFix = '') {
  60. if($postFix !== '') {
  61. $postFix = '.' . ltrim($postFix, '.');
  62. $postFix = str_replace(['\\', '/'], '', $postFix);
  63. $absolutePath .= '-';
  64. }
  65. return $absolutePath . $postFix;
  66. }
  67. /**
  68. * Create a temporary file and return the path
  69. *
  70. * @param string $postFix Postfix appended to the temporary file name
  71. * @return string
  72. */
  73. public function getTemporaryFile($postFix = '') {
  74. if (is_writable($this->tmpBaseDir)) {
  75. // To create an unique file and prevent the risk of race conditions
  76. // or duplicated temporary files by other means such as collisions
  77. // we need to create the file using `tempnam` and append a possible
  78. // postfix to it later
  79. $file = tempnam($this->tmpBaseDir, self::TMP_PREFIX);
  80. $this->current[] = $file;
  81. // If a postfix got specified sanitize it and create a postfixed
  82. // temporary file
  83. if($postFix !== '') {
  84. $fileNameWithPostfix = $this->buildFileNameWithSuffix($file, $postFix);
  85. touch($fileNameWithPostfix);
  86. chmod($fileNameWithPostfix, 0600);
  87. $this->current[] = $fileNameWithPostfix;
  88. return $fileNameWithPostfix;
  89. }
  90. return $file;
  91. } else {
  92. $this->log->warning(
  93. 'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions',
  94. [
  95. 'dir' => $this->tmpBaseDir,
  96. ]
  97. );
  98. return false;
  99. }
  100. }
  101. /**
  102. * Create a temporary folder and return the path
  103. *
  104. * @param string $postFix Postfix appended to the temporary folder name
  105. * @return string
  106. */
  107. public function getTemporaryFolder($postFix = '') {
  108. if (is_writable($this->tmpBaseDir)) {
  109. // To create an unique directory and prevent the risk of race conditions
  110. // or duplicated temporary files by other means such as collisions
  111. // we need to create the file using `tempnam` and append a possible
  112. // postfix to it later
  113. $uniqueFileName = tempnam($this->tmpBaseDir, self::TMP_PREFIX);
  114. $this->current[] = $uniqueFileName;
  115. // Build a name without postfix
  116. $path = $this->buildFileNameWithSuffix($uniqueFileName . '-folder', $postFix);
  117. mkdir($path, 0700);
  118. $this->current[] = $path;
  119. return $path . '/';
  120. } else {
  121. $this->log->warning(
  122. 'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions',
  123. [
  124. 'dir' => $this->tmpBaseDir,
  125. ]
  126. );
  127. return false;
  128. }
  129. }
  130. /**
  131. * Remove the temporary files and folders generated during this request
  132. */
  133. public function clean() {
  134. $this->cleanFiles($this->current);
  135. }
  136. /**
  137. * @param string[] $files
  138. */
  139. protected function cleanFiles($files) {
  140. foreach ($files as $file) {
  141. if (file_exists($file)) {
  142. try {
  143. \OC_Helper::rmdirr($file);
  144. } catch (\UnexpectedValueException $ex) {
  145. $this->log->warning(
  146. "Error deleting temporary file/folder: {file} - Reason: {error}",
  147. [
  148. 'file' => $file,
  149. 'error' => $ex->getMessage(),
  150. ]
  151. );
  152. }
  153. }
  154. }
  155. }
  156. /**
  157. * Remove old temporary files and folders that were failed to be cleaned
  158. */
  159. public function cleanOld() {
  160. $this->cleanFiles($this->getOldFiles());
  161. }
  162. /**
  163. * Get all temporary files and folders generated by oc older than an hour
  164. *
  165. * @return string[]
  166. */
  167. protected function getOldFiles() {
  168. $cutOfTime = time() - 3600;
  169. $files = [];
  170. $dh = opendir($this->tmpBaseDir);
  171. if ($dh) {
  172. while (($file = readdir($dh)) !== false) {
  173. if (substr($file, 0, 7) === self::TMP_PREFIX) {
  174. $path = $this->tmpBaseDir . '/' . $file;
  175. $mtime = filemtime($path);
  176. if ($mtime < $cutOfTime) {
  177. $files[] = $path;
  178. }
  179. }
  180. }
  181. }
  182. return $files;
  183. }
  184. /**
  185. * Get the temporary base directory configured on the server
  186. *
  187. * @return string Path to the temporary directory or null
  188. * @throws \UnexpectedValueException
  189. */
  190. public function getTempBaseDir() {
  191. if ($this->tmpBaseDir) {
  192. return $this->tmpBaseDir;
  193. }
  194. $directories = [];
  195. if ($temp = $this->config->getSystemValue('tempdirectory', null)) {
  196. $directories[] = $temp;
  197. }
  198. if ($temp = \OC::$server->getIniWrapper()->get('upload_tmp_dir')) {
  199. $directories[] = $temp;
  200. }
  201. if ($temp = getenv('TMP')) {
  202. $directories[] = $temp;
  203. }
  204. if ($temp = getenv('TEMP')) {
  205. $directories[] = $temp;
  206. }
  207. if ($temp = getenv('TMPDIR')) {
  208. $directories[] = $temp;
  209. }
  210. if ($temp = sys_get_temp_dir()) {
  211. $directories[] = $temp;
  212. }
  213. foreach ($directories as $dir) {
  214. if ($this->checkTemporaryDirectory($dir)) {
  215. return $dir;
  216. }
  217. }
  218. $temp = tempnam(dirname(__FILE__), '');
  219. if (file_exists($temp)) {
  220. unlink($temp);
  221. return dirname($temp);
  222. }
  223. throw new \UnexpectedValueException('Unable to detect system temporary directory');
  224. }
  225. /**
  226. * Check if a temporary directory is ready for use
  227. *
  228. * @param mixed $directory
  229. * @return bool
  230. */
  231. private function checkTemporaryDirectory($directory) {
  232. // suppress any possible errors caused by is_writable
  233. // checks missing or invalid path or characters, wrong permissions etc
  234. try {
  235. if (is_writeable($directory)) {
  236. return true;
  237. }
  238. } catch (\Exception $e) {
  239. }
  240. $this->log->warning('Temporary directory {dir} is not present or writable',
  241. ['dir' => $directory]
  242. );
  243. return false;
  244. }
  245. /**
  246. * Override the temporary base directory
  247. *
  248. * @param string $directory
  249. */
  250. public function overrideTempBaseDir($directory) {
  251. $this->tmpBaseDir = $directory;
  252. }
  253. }