<?php /** * Keeps files on local disk, but sudo-s as the daemon user for it. * * @task internal Internals */ final class LocalDiskSudoFileStorageEngine extends PhabricatorFileStorageEngine { /* -( Engine Metadata )---------------------------------------------------- */ public function getEngineIdentifier() { return 'local-disk-sudo'; } public function getEnginePriority() { return 4; // Just below the regular disk one. } public function canWriteFiles() { $path = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); $user = PhabricatorEnv::getEnvConfig('phd.user'); return (bool)strlen($path) && (bool)$user; } /* -( Managing File Data )------------------------------------------------- */ /** * Write the file data to local disk. Returns the relative path as the * file data handle. * @task impl */ public function writeFile($data, array $params) { $root = $this->getLocalDiskFileStorageRoot(); // Generate a random, unique file path like "ab/29/1f918a9ac39201ff". We // put a couple of subdirectories up front to avoid a situation where we // have one directory with a zillion files in it, since this is generally // bad news. do { $name = md5(mt_rand()); $name = preg_replace('/^(..)(..)(.*)$/', '\\1/\\2/\\3', $name); if (!Filesystem::pathExists($root.'/'.$name)) { break; } } while (true); AphrontWriteGuard::willWrite(); $binary = Filesystem::resolveBinary('write-file-to-storage.sh'); $command = csprintf('%s %s', $binary, $root.'/'.$name); $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); id(new ExecFuture('%C', $command)) ->write($data) ->resolvex(); return $name; } /** * Read the file data off local disk. * @task impl */ public function readFile($handle) { $path = $this->getLocalDiskFileStorageFullPath($handle); return Filesystem::readFile($path); } /** * Deletes the file from local disk, if it exists. * @task impl */ public function deleteFile($handle) { $path = $this->getLocalDiskFileStorageFullPath($handle); if (Filesystem::pathExists($path)) { AphrontWriteGuard::willWrite(); $binary = Filesystem::resolveBinary('rm-file-from-storage.php'); $command = csprintf('%s %s', $binary, $path); $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); execx('%C', $command); } } /* -( Internals )---------------------------------------------------------- */ /** * Get the configured local disk path for file storage. * * @return string Absolute path to somewhere that files can be stored. * @task internal */ private function getLocalDiskFileStorageRoot() { $root = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); if (!$root || $root == '/' || $root[0] != '/') { throw new PhabricatorFileStorageConfigurationException( pht( "Malformed local disk storage root. You must provide an absolute ". "path, and can not use '%s' as the root.", '/')); } return rtrim($root, '/'); } /** * Convert a handle into an absolute local disk path. * * @param string File data handle. * @return string Absolute path to the corresponding file. * @task internal */ private function getLocalDiskFileStorageFullPath($handle) { // Make sure there's no funny business going on here. Users normally have // no ability to affect the content of handles, but double-check that // we're only accessing local storage just in case. if (!preg_match('@^[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{28}\z@', $handle)) { throw new Exception( pht( "Local disk filesystem handle '%s' is malformed!", $handle)); } $root = $this->getLocalDiskFileStorageRoot(); return $root.'/'.$handle; } } /** // rm-file-from-storage.php: #!/usr/bin/env php <?php $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; $filename = $argv[1]; $root = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); if (substr($filename, 0, strlen($root)) != $root) { throw new Exception('File is not in storage directory!'); } if (strstr($filename, '..') != false) { throw new Exception('invalid filename!'); } execx('rm %s', $filename); */ /** // write-file-to-storage.sh: #! /usr/bin/env bash set -e # Saves data from STDIO to this file # run this as the daemon user DEST_FILE=$1 # abs path if [[ -z $DEST_FILE ]] then echo 'needs dst filename!' >&2 exit 3 fi mkdir -p $(dirname $DEST_FILE) cat - > $DEST_FILE */