PHP Windows, rmdir of a symlinked folder - php

I have recently been struggling with removal of symlinked folders with content on windows in PHP.
The process I am doing is:
1. symlink files/folders from location A to location B (all good)
2. unlink all files/folders from location B
Now this is where things get tricky.
My code:
echo("\n unlinking: ".$pre.$folder.'/'.$elem);
if(file_exists($pre.$folder.'/'.$elem)){
if(isWindows()){
if(is_dir($pre.$folder.'/'.$elem)){
rmdir($pre.$folder.'/'.$elem);
} else {
unlink($pre.$folder.'/'.$elem);
}
} else {
unlink($pre.$folder.'/'.$elem);
}
} else {
echo("\n -> Not there. \n");
}
Everything works properly if the target is a file or an empty folder. When the symlinked folder has contents however, I get a warning that I can't remove a non-empty folder and the folder is not removed.
Warning: rmdir(dirname): Directory not empty
Which means that a symlinked folder with contents on windows is non-removable when using rmdir(the recommended operation).
I can remove that folder manually in windows explorer and that works properly(removes a symlink only).
Would appreciate help,
Sivael.

When deleting a symlink, you need to treat it as a file, not a directory. So, you need to use unlink, rather than rmdir!
HTH :)

Found out what was going on.
It turns out that it was not PHP related after all - those folders were under version control in TortoiseSVN and NetBeans, which happened to mess with the symlinks somehow.
Can't replicate the behaviour now.
Thanks:)

Related

PHP renaming index files

I'm trying to switch index files using PHP and have gone for a method where I rename respective index files, like this:
<?php
rename("/index.html", "/REALindex.html");
rename("/index2.html","index.html");
?>
The PHP script is located in a folder child of the index files, but when I run this php nothing happens to the files, so I am both wondering how I'm supposed to debug and also why it's not working..?
Thanks a lot.
If your PHP script has permissions to edit the local directories you're running in (based upon the comment you made), your HTML files are one level up from the php, so try:
rename("../index.html", "../REALindex.html");
rename("../index2.html","../index.html");
Note the forward slash at the beginning of the paths. Could it be that this is accidentally and you're unwillingly trying to move files from/to the filesystem root, where they just aren't?
SOULTION:
chmod -R 777 /var/www/html
+
rename("../index.html", "../REALindex.html");
rename("../index2.html","../index.html");

PHP and Symlinks in Windows

I have a directory tree with symlinks in it. They are called with require_once, but sometimes they are referred to with 'm' and sometimes 'mydir'. 'm' and 'mydir' are symlinked, but when the require_once is called twice, it treats them as different files and the code errors.
require_once("m/myfile.php");
require_once("mydir/myfile.php);
I only want the file included once but it tries to do it twice.
Some years later under windows 10 I had similar problems:
I checked out our project via git, symlinks activated in git
In the project some directory symlinks are used
Apache and bash under windows were working without any problems with the generated symlinks
PHP, Windows Explorer and CMD could not deal with the symlinks
The problem was, that the symlinks were created as "file" symlinks, but were pointing to directories.
After removing the symlinks and recreating them manually with
mklink /D link target
everything was working as expected.
No symlinks in Windows, sorry.
If you have checked out or downloaded the code from let's say github and then dropped it to a NTFS file system, your download program may be creating the file twice once for the link and once for the linked file. I've seen this often
I would use my own require function / autoloading if fully OO http://php.net/manual/en/language.oop5.autoload.php, or a fix path function.
i.e.
//custom require
function myRequireOnce($filePath)
{//ensure only one version of sym path is ever used
$filepath = str_replace('mydir', 'm' , $filepath); //possibly too simple
require_once($filepath);
}
//fix path function
_fp($path)
{
return str_replace('mydir', 'm' , $filepath);
}
require_once(_fp("m/myfile.php"));
In all cases the object is to ensure that only one path is ever actually required, and conversion takes place on the alternative versions.

drupal file_check_path return false

I am working on files in drupal. i use local wamp server with drupal6.
my drupal path is localhost/drupal6 .i have a file in this path <drupal root >\files\images\111.jpg .
$image='/drupal6/files/images/111.jpg';
I want copy(or move) this file to another sub folder in this path but give me error in php copy($image,'anotherdestination') or drupal_copy($image,'anotherdestination',FILE_EXISTS_RENAME).
when i use var_dump(file_check_path($image)); it return me false .
the files and iamges folder have all permission for any user in windows.
where is the problem?
notice: i wrote all of this code in a function in a custom module with path sites/all/module/mymodule/mymodule.module
There's a difference between how files are accessed over HTTP and via the file system. Over web /drupal6/files/images/111.jpg is perfectly fine, since it'll be evaluated as http://example.com/drupal6/files/images/111.jpg. But I bet you do not have a drupal6 directory in the root of your file system.
You probably want something like this.
copy('c:\drupal6\files\images\111.jpg', 'c:\drupal6\files\other_images\111.jpg');
I'm not entirely sure about paths in Windows but I think that should be correct.

PHP Can't find tmp directory

I am having problems with functions that create files in the tmp directory such as tmpfile() and tempnam(). They all seem to fail to write to tmp and return false. upload_tmp_dir is set in php ini and file uploads work fine.
When debugging this error I found that sys_get_temp_dir() gets the location of the tmp directory unfortunately it's not supported in my PHP version (5.1.6). I also saw that using the following method replaces the functionality of sys_get_temp_dir():
if ( !function_exists('sys_get_temp_dir')) {
function sys_get_temp_dir() {
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
$tempfile=tempnam(__FILE__,'');
if (file_exists($tempfile)) {
unlink($tempfile);
return realpath(dirname($tempfile));
}
return null;
}
}
But there is no reference to a tmp directory in the $_ENV array and tempnam() fails as I mentioned before.
Also open_basedir is not set which I've heard can cause similar problems
How can I find out where the tmp directory is or whether it is even set?
Is this a apache server configuration issue or a PHP one?
Thanks for your help
I am running Ubuntu 18.04 and I could create/modify files in the /tmp directory when I ran the PHP script from the CLI, but when I tried accessing the same script as a web page, I could never find the file that was being created. It turns out that Apache by default will create a private tmp directory. The following post provided some insight on the problem Odd Bits - Private /tmp directory. However, the /usr/lib/systemd directory mentioned in the post did not contain any services for http or apache2 on my machine. To help track down the problem I executed the following command:
sudo find / -mount -type f -exec grep -e "PrivateTmp" '{}' ';' -print
and found in /lib/systemd/system/apache2.service the PrivateTmp=true mentioned in the Odd Bits post. Copying the file from /lib/systemd/system to /etc/systemd/system/ and changing true to false and executing
systemctl daemon-restart
systemctl restart apache2
fixed the problem. A person wiser than me suggested copying the file to /etc instead of editing it in /lib was the correct course of action because /lib is 'owned' by the packages and local edits should be performed in /etc. systemd man page describes the systemd configuration processing in gory details.
you can set the upload temp dir in your php.ini -
something like that should work:
upload_tmp_dir=/your-www/tmp/
Also, in case you can't edit the php.ini or don't want to do it globally you can use this in the beginning of your script:
ini_set('upload_tmp_dir','/your-home-www/tmp/');
TMP, TEMP (and maybe TMPDIR) are valid on Windows only and usually pointing to C:\Windows\TEMP. On Linux default temp location is /tmp. To workaround this (works with tempnam() function) you can create a temp folder somewhere within your website space, specify appropriate access permissions and pass this as first parameter to the above function.
Not a great solution but better than nothing.
Probably not the cleanest but this works on my old 5.1.6 install:
function get_temp_path() {
static $path = null;
if ($path !== null) return $path;
$file = tmpfile();
$meta = stream_get_meta_data($file);
fclose($file);
$path = dirname($meta['uri']);
return $path;
}
I have the same problem and the solution is to change the apache configuration to expose the TEMP variable to PHP, see this post.
Tip for newbies like me: I THOUGHT that PHP couldn't move stuff from my temporary folder, but I was just confused because of the relative positions of folders. This may apply to someone else, so I'll explain, even though it's very tangentially related to this specific question (because this specific question is a likely search result for other people like me who are newbies).
My problem is that I was echoing an upload form FROM a functions.php file inside of /var/www/html/ TO a profile.php file in /var/www/html/user/ which CALLED an uploadphoto.php file in /var/www/html/. Uploaded files were ultimately intended to land in /var/www/html/uploads. This ultimately meant that most of my references to both uploadphoto.php AND uploads/ in functions.php were written "../uploadphoto.php" or "../uploads/[etc.jpg]", respectively, in order to step back into html/ from html/user/ (where the echoed code landed in html/user/profile.php). This led me to intuitively use the following command inside of uploadphoto.php without thinking it through:
move_uploaded_file($_FILES["file"]["tmp_name"][0], "../uploads/$filename")
See the problem? uploadphoto.php is in the same directory as uploads/, so I did not need the ../ here. For hours, I was sure I was messing up my folder permissions again, because I am new to image uploading. I had forgotten to check for more simple-minded errors. Hopefully this helps someone else.

Recognizing the root dir in PHP

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.

Categories