Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to resolve a relative path (like realpath) for non-existing files?

Tags:

I'm trying to enforce a root directory in a filesystem abstraction. The problem I'm encountering is the following:

The API lets you read and write files, not only to local but also remote storages. So there's all kinds of normalisation going on under the hood. At the moment it doesn't support relative paths, so something like this isn't possible:

$filesystem->write('path/to/some/../relative/file.txt', 'file contents'); 

I want to be able to securely resolve the path so the output is would be: path/to/relative/file.txt. As is stated in a github issue which was created for this bug/enhancement (https://github.com/FrenkyNet/Flysystem/issues/36#issuecomment-30319406) , it needs to do more that just splitting up segments and removing them accordingly.

Also, since the package handles remote filesystems and non-existing files, realpath is out of the question.

So, how should one go about when dealing with these paths?

like image 218
Frank de Jonge Avatar asked Dec 11 '13 15:12

Frank de Jonge


People also ask

How do you convert relative path to absolute path?

The absolutePath function works by beginning at the starting folder and moving up one level for each "../" in the relative path. Then it concatenates the changed starting folder with the relative path to produce the equivalent absolute path.

How do you set a relative path?

You can also set this option by right-clicking the script tool, clicking Properties, then clicking the General tab. At the bottom of the dialog box, check Store relative path names (instead of absolute paths).


2 Answers

To quote Jame Zawinski:

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

protected function getAbsoluteFilename($filename) {   $path = [];   foreach(explode('/', $filename) as $part) {     // ignore parts that have no value     if (empty($part) || $part === '.') continue;      if ($part !== '..') {       // cool, we found a new part       array_push($path, $part);     }     else if (count($path) > 0) {       // going back up? sure       array_pop($path);     } else {       // now, here we don't like       throw new \Exception('Climbing above the root is not permitted.');     }   }    // prepend my root directory   array_unshift($path, $this->getPath());    return join('/', $path); } 
like image 129
Beat Christen Avatar answered Sep 18 '22 20:09

Beat Christen


I've resolved how to do this, this is my solution:

/**  * Normalize path  *  * @param   string  $path  * @param   string  $separator  * @return  string  normalized path  */ public function normalizePath($path, $separator = '\\/') {     // Remove any kind of funky unicode whitespace     $normalized = preg_replace('#\p{C}+|^\./#u', '', $path);      // Path remove self referring paths ("/./").     $normalized = preg_replace('#/\.(?=/)|^\./|\./$#', '', $normalized);      // Regex for resolving relative paths     $regex = '#\/*[^/\.]+/\.\.#Uu';      while (preg_match($regex, $normalized)) {         $normalized = preg_replace($regex, '', $normalized);     }      if (preg_match('#/\.{2}|\.{2}/#', $normalized)) {         throw new LogicException('Path is outside of the defined root, path: [' . $path . '], resolved: [' . $normalized . ']');     }      return trim($normalized, $separator); } 
like image 43
Frank de Jonge Avatar answered Sep 22 '22 20:09

Frank de Jonge