I'm writing a PHP function that will delete all the files in the directory that's passed to it (and eventually, possibly expand it to a recursive delete). To be safe, I want to make sure that, through a bug or something, I don't try to delete anything if the directory passed in is the root directory.
File permissions should protect me to a large extent, but just in case, and especially if I expand it to a recursive delete I just want to take that extra step.
As a complicating factor, this code may be run in a windows machine or a linux machine, so the root directory may look like 'C:\' or '/'. I assume there are other ways that really refer to the root as well, possibly 'c:\temp..'
So, is there a reliable way in PHP to recognize that a dir spec resolves to the root of the file system?
Elaboration...
I'm writing PHPUnit tests for a web app and I'm trying to create a framework where the state of the app is backed up before the tests are run and restored afterwards. The app allows users to upload files. Depending on what the file is it is copied to one of several possible directories.
To save and restore the state of the app those directories need to be copied somewhere, the tests run, then the directories need to have their files deleted and retreived from the backup.
The location of these directories can vary from one machine to another and I know that some people put them outside of the web app. There is a configuration file that can be read by the test that gives the location of those directories for the given machine.
If I don't restrict all these directories to a specific dir tree it's difficult to do the jailing. If I do restrict these directories to a specific dir tree then some people will have to reconfigure their machines.
You should have a defined root folder, which you never go above, a.k.a. jailing. The root folder is not the only folder where severe damage can be done.
Edit.
Although I still advocate using some sort of jailing, I suppose you could recognize the root folder by stripping out any drive-letters and translating \ to /. The root folder would then always be a single /.
function isRootFolder($dirpath) {
list($drive, $path) = explode(':', str_replace('\\', '/', $dirpath), 2);
return $path == '/';
}
Try, this function:
function is_root_dir($path)
{
$clean_path = realpath($path);
if($clean_path == '/' || preg_match('/[a-z]:\\/i', $clean_path))
{
return true;
}
else
{
return false;
}
}
It's not tested, I just wrote it in the editor here. realpath() resolves the path, folowing simbolic links and resolving stuff like: c:\temp.. == c:\
Edit: In the end you should folow the advice that nikc gave you, define a list of directories that are safe to delete.
I use this:
if (dirname($target)==$target) { // you're at the root dir
(is portable between Microsoft and everything else)
C.
Related
This question already has answers here:
What is double dot(..) and single dot(.) in Linux?
(4 answers)
Closed 4 years ago.
I am working on a PHP website where I needed to get a list of the files in the Images directory. The example in PHP.net shows the addressing with just a forward slash as shown in the example. This addressing pattern did not work. Instead the address needs to start with ./ . What is the difference?
<?php
$dir = '/tmp';
$files1 = scandir($dir);
$files2 = scandir($dir, 1);
print_r($files1);
print_r($files2);
?>
A search for other questions have addressed single dot and double dot, but I don't see posts for a forward slash with no dot.
the . indicates the current directory. Assume, for instance, that your current path is /var/www:
If you say ./tmp the directory you are looking for is /var/www/tmp
If you say /tmp you are looking for /tmp (starting from the root)
Maybe you're a Windows user and you are not familiar with the way Linux addresses files, but essentially there are no drive letters. / is the root of the file system, basically equivalent to C:\ on Windows (though not quite). It also works on Windows except it will refer to the root of the partition your script is running in as Windows separates each partition on a different drive letter.
There's a standard folder in the root of the file system called tmp, and if you want to refer to it you want to specify full path, /tmp.
Just using tmp or ./tmp will refer to a tmp folder on your local path, which is a completely different folder just with the same name (that might not even exist).
When you don't want to specify a full path but somewhere relative or local path instead, you don't put / in the beginning. You might just put nothing, or you can put ./ to explicitate this is a path relative to your current working directory which is ..
It doesn't matter in which folder your script working, . always represents it. It is actually unnecessary to put ./ in most cases since relative paths are implicit.
On a relate note, .. represents the parent folder.
Example, my script is /var/www/script.php, when I run it:
. is the folder /var/www.
fopen('note.txt', 'r') will open file /var/www/note.txt for reading.
fopen('./note.txt', 'r') will do the exact same thing.
.. is the folder /var.
../../tmp is the same as /tmp.
Note the current working directory represented by . remains constant even if you include() a script from a subfolder.
This might get things confusing because . may not be the folder the script you included is in. To workaround this problem, use __DIR__ to get the folder your script is in, it will get the current folder of your script even if you are calling it through a include() or calling it from a different directory.
/ denotes the root directory of your server. As in /var/www/.
./ denotes the current directory that the script is executing in. This is the equivalent of getcwd(). For example, you're currently in /php/scripts, it will refer to /var/www/php/scripts.
../ denotes the parent folder, and can be chained to move up multiple levels.
The use of each depends on which file or folder you're trying to manipulate from which script.
If you're trying to access /var/www/tmp from /var/www/php/scripts/script.php, you can use either:
../../tmp
/tmp
Note that if you're unsure how many levels you need to 'move up', you can affix an infinite number of ../; it won't 'break out' of the root. Having said that, referencing directly from the root is often much more straight-forward.
/ in the beginning of a link to get to the root folder doesn't work in php include.
for example "/example/example.php"
What is the solution?
I'm assuming by root folder you mean your web document root, rather than filesystem root.
To that end, you can either
add the web root folder to the include path, and include('example/example.php')
or you can include($_SERVER['DOCUMENT_ROOT'].'/example/example.php')
I had this issue too. Paul Dixon's answer is correct, but maybe this will help you understand why:
The issue here is that PHP is a server side language. Where pure HTML documents can access files based on the root url you set up on the server (ie. to access an image from any sub-directory you're on you would use /images/example.jpg to go from the top directory down), PHP actually accesses the server root when you use include (/images/example.jpg)
The site structure that you have set up actually lies within a file system in the Apache Server. My site root looks something like this, starting from the server root and going down:
/home2/siteuserftp/public_html/test/
"test" represents your site root
So to answer your question why your PHP include isn't getting the result you want (it is working exactly as it should) is because you're asking the PHP code to try and find your file at the server root, when it is actually located at the HTML root of your site which would look something like the above.
Your file would be based on the site root of "test/" and would look something like this:
/home2/siteuserftp/public_html/test/about/index.php
The answer Paul Dixon provided:
include($_SERVER['DOCUMENT_ROOT'].'/example/example.php')
is exactly what will fix your problem (don't worry about trying to find the document root to replace 'DOCUMENT_ROOT', PHP will do it for you. Just make sure you have 'DOCUMENT_ROOT' literally in there)
EDIT:
More information DOCUMENT_ROOT and other PHP SERVER variables can be found here
include() (and many other functions like require(), fopen(), etc) all work off the local filesystem, not the web root.
So, when you do something like this
include( "/example/example.php" );
You're trying to include from the root of your *nix machine.
And while there are a multitude of ways to approach what you're doing, Paul Dixon's suggestions are probably your best bets.
Every web server has a public_html folder, in which you usually keep your files etc. By using /, you will not get to public_html, instead you direct towards the main (unaccesible) root. So, use $_SERVER['DOCUMENT_ROOT']."/your/locati.on" instead
I solved this on a machine running Windows and IIS with the following:
<?php
$docroot = 'http://www.example.com/';
include ($docroot.'inc-header.php');
?>
If you're on a local dev machine, you can force your domain to point to localhost by adding the following in C:\Windows\System32\drivers\etc\hosts
127.0.0.1 www.example.com
Also, you'll need to enable allow_url_include in php.ini like so
allow_url_include = On
For me, the following trick worked.
I'm using Windows with IIS, so DOCROOT is C:\Inetpub\wwwroot.
do subst of C:\Inetpub\wwwroot to a drive. Let it be W: (WEB contents).
subst W: C:\Inetpub\wwwroot
edit php.ini this way: append W:\ to include_path, change doc_root to W:\
include_path = ".;c:\php\active\includes;W:\"
doc_root = W:\
put subst command into CMD file within Startup folder to make mapping automatically.
Now, both versions allowed:
include '/common/common.inc'; // access to mapped W: root
include 'common/common.inc'; // access to W: within include_path
some versions of PHP may have the delimiter at the end of document root while others may not. As a practical matter you may want to use:
$r = trim(filter_input(INPUT_SERVER, 'DOCUMENT_ROOT', FILTER_SANITIZE_STRING));
if (substr($r, 0, 1) == '/')
{
define("PATCH_SEPARATOR", "/");
}
else
{
define("PATCH_SEPARATOR", "\\");
}
if (substr($r, -1) == PATCH_SEPARATOR)
{
include_once ($r . 'example/example.php');
}
else
{
include_once ($r . PATCH_SEPARATOR . 'example/example.php');
}
maybe it's a bit unconventional
If I have a case like
/var/www/namedir/ <= root
/var/www/namedir/example/example.php <= file to include
-- directory when i need the include --
/var/www/namedir/dir1/page.php
/var/www/namedir/dir1/dirA/page.php
/var/www/namedir/dir1/dirB/page.php
the solution that I use is simple.
get the path before the "Dir1"
something like this
include (substr(dirname(__FILE__),0,strpos(dirname(__FILE__), '/dir1'))."/example/example.php");
I found it usefull id i need to rename the main subdir
for example from
/var/www/namesite/internalDirA/dirToInclude/example.php
/var/www/namesite/internalDirA/dir1/dirA/example.php
/var/www/namesite/internalDirA/dir1/dirB/example.php
TO
/var/www/namesite/dirReserved/dirToInclude/example.php
/var/www/namesite/dirReserved/dir1/dirA/example.php
/var/www/namesite/dirReserved/dir1/dirB/example.php
This answer is not really for the root directory, but one workaround is to use ../ to jump to the parent directory.Of course, you need to know the file structure for this approach though.
For example, you could use:
include('../parent.php');
include('../../grand_parent.php');
I'm creating a .php file that will be uploaded to the root directory of a server. I need that .php file to then figure out the path to the public_html folder or it's equivalent.
I need to do this because I want my .php file to be able to be uploaded to the root and used on any hosting account. Because many hosting companies use different file paths to the public_html folder or even call it something different, I'm trying to figure out how to detect it.
Preferable there is a server variable or easy test to do this. If not, the public_html folder will always contain a particular file so maybe I could search for this particular file and get the path that way. I'm just worried about a filename search being heavy on memory.
The .php file that is being executed is located inside the ROOT directory and needs to locate the public_html folder.
Like this: /home/user/file.php
needs to detect
/home/user/public_html/ or /home/user/var/www/ or /home/user/website.com/html/ etc.
The challenge with this is that a server can have very many public_html's so outside of the context of a request there is no real way to find out what that is.
One thing that you might be able to do to get this information from a php script (if you know the url to get to the host) is to create a php file called docroot.php that looks like this.
<?php
if ($_SERVER["REMOTE_ADDR"] == '127.0.0.1'){
echo $_SERVER["DOCUMENT_ROOT"];
}
Then within your file.php your would do something like
$docRoot = trim(file_get_contents("http://www.mydomain.com/docroot.php"));
This makes the assumption that the server can resolve to itself via the local interface by name.
I found this website which provided me with the only good solution I have found after scouring the web...
$root = preg_replace("!${_SERVER['SCRIPT_NAME']}$!", "", $_SERVER['SCRIPT_FILENAME']);
The way this works is by getting the full path of the file and then removing the relative path of the file from the full path.
Currently our PHP app requires to be set up in the web server's document root (usually by configuring Apache's virtual hosts).
The main reason for that dependency is that our URL-generation logic assumes that all URLs can be accessed through the absolute path /. That makes for easy linking to resources such as images, and pages.
The user may be visiting the app from different sub-folders, so we cannot assume a simple relative path to work.
How would we decouple the app from needing to run in the document root of the web server? Would you suggest parsing $_SERVER['DOCUMENT_ROOT'] in that URL controller to try to decide how far down in the filesystem the file is being accessed? Right now, I don't see a sure-fire way of doing that parsing. Also a complication here is that we use Apache's ReRewrite so URLs don't necessarily match the file system.
I generally use a Simple and Stupid method for this purpose like:
If users can access indeterministically from different entry points of the application, the entry points should know "where they are" relatively to the application root.
First I should know the relative position of my current script w.r. application root so I statically write into the script i.e;
For a script that should be at
...approotdirname/appsubdir1name/appsubdir2name/here.php
.
$relPath = 'approotdirname/appsubdir1name/appsubdir2name';
Note that if your script is at the application root, the $curPath calculated below will be directly your application position on the server.
Second I should learn the current script file path from Php with a uniform format:
$curPath = str_replace('\\','/',dirname(__FILE__));
(str_replace is for replacing \ with / in case they exist.)
In fact a copy of the $relPath (if figured correctly) should exist at the end of the $curPath.
Then I will find the position of $relPath in the $curPath:
$p = stripos($curPath, $relPath, 0);
if(($p === false)||($p < 0)){echo "Relative path: $relPath is invalid."; exit(0);}
Now when we subtract $relPath from $curPath we get where on earth is our application root positioned on the server with a little checking.
$rootPath = ($p > 0)? substr($curPath, 0, $p-1): '/';
I hope this helps.
Please note that restricting user access through a single point like an index.php at application root is generally accepted as a better practice. There at the entry point you can get
$rootPath = str_replace('\\','/',dirname(__FILE__)).'/';
and use it all through your application for file access and includes. As a bonus, you can move your entire application without breaking its file relations.
I see two options:
Refactor the existing program to route all requests through a single script (suggested above, but it's not practical for our project)
Use smudge and clean attribute filters, requiring some relativley advanced git.
I wrote a CMS in PHP. It works fine on most servers but I encountered a strange problem on my latest hosting account. This is either a path problem or a coding problem. The latter seems to be OK as this script works fine on all my other accounts, which is why I'm asking for help.
When I first install my CMS tool I run a script called "inventory.php" in which I attempt to get and display all the directories on the server from the location of my script, which is 2 or 3 directories down from the root, depending on the server. I try to use a global path that goes up to the root and from it to return all the directories it can find. If this file works then the entire CMS works; if not... well that's why I'm here.
Here's the code that scans the directories:
$main_root = realpath('../../');
echo '<b style="color:orange;">All The dirs on this server:</b><hr><br>';
$whats_on_the_server = array_filter(glob($main_root.'/*'), 'is_dir');
foreach($whats_on_the_server as $on_server) {
$on_server = trim($on_server);
if(stristr($on_server,'.')){
$arr1 = preg_split('^/^',$on_server);
echo $arr1[4].'<br>';
}
}
The root is: chroot/home/account/ under which all my folders are located. I can't run a script from that directory, so I must access it from elsewhere. This works fine on other servers but on the one I currently use, it doesn't return anything if the path is set as above. It gets the directory contents if I set the path to a subfolder on the specific server, such as:
$main_root = realpath('../../SomeDir');
I must however get the name of all the directories located on the root.
So probably you don't have the privileges to access the root but do have privilegs to access the given subdirectories?
By the way, you can go to root by simply doing
$main_root = realpath('/');
which is always your most upper path!
You should use:
$main_root = dirname(__FILE__).'../../';
where __FILE__ is a PHP constant for the current file.