Context
PHP 5.6
IIS
Windows 11
Issue
I am trying to write a file in a specific folder but it gives me permission denied.
So, I verified the permissions and everything seemed all right. Because this is in a development environment, I decided to change the permissions to be "Every one can do anything" to the "root" folder where all my files are written. I tried to replace all the permissions underneath. I tried to remove the inherited permissions. Nothing does it.
I then tried to write a file in the "root" folder. It worked.On the subfolder, it worked. On the subsubfolder. It worked. In the subfolder chain there is a point when it doesn't work, but before the real subfolder.
Here is the path hierachy when it crashes.
$f = new \wee\common\IO\File();
$f->write("DRIVE:/BASE_PATH/files/-/00.jpg", "hello"); // WORKS
$f->write("DRIVE:/BASE_PATH/files/-/mod/00.jpg", "hello"); // WORKS
$f->write("DRIVE:/BASE_PATH/files/-/mod/com.ci.company/00.jpg", "hello"); // WORKS
$pathLength = strlen("DRIVE:/BASE_PATH/files/-/mod/com.ci.company/site/00.jpg"); // Real path length is 85
$f->write("DRIVE:/BASE_PATH/files/-/mod/com.ci.company/site/00.jpg", "hello"); // FAILS
$f->write("DRIVE:/BASE_PATH/files/-/mod/com.ci.company/site/WorkersManager/00.jpg", "hello");
$f->write("DRIVE:/BASE_PATH/files/-/mod/com.ci.company/site/WorkersManager/workers/00.jpg", "hello");
The class \wee\common\IO\File is my creation, but works at a lot of other places.
The exact error message I am getting is:
fopen(DRIVE:/BASE_PATH/files/-/mod/com.ci.company/site/00.jpg): failed to open stream: Permission denied
Just to be 100% clear: The "root" folder is DRIVE:/BASE_PATH/files/
Edit #1
Here is the implementation of the write method of the File class.
public function write($fileName, $data, $lock = false) {
$this->_write($fileName, $data, $lock);
}
private function _write($fileName, $data, $lock = false, $openMode = 'w') {
if ($data === null)
$data = "";
$fh = fopen($fileName, $openMode) or die("can't open file"); // FAILS HERE
if ($lock) {
flock($fh, LOCK_EX);
}
fwrite($fh, (is_array($data) ? join("\n", $data) : $data));
if ($lock) {
fflush($fh);
flock($fh, LOCK_UN);
}
fclose($fh);
}
The issue was coming from PHP 5.6.26. Using PHP 5.6.40 fixed it.
I reset to my original permissions and everything is fine!
Related
Today I'm battling against ssh with huge success if it wasn't for ssh2_scp_send() method. This is the first time I work integrating ssh with PHP, so you may find multiple erros in the code that I have yet to discover, since I am currently stuck at that part.
I am using a dummy array imitating the one I must get from a couple of forms and the auth is working fine, although I had a little problem back there given that I had to use 'root' as user for the public key to be recognised as correct when comparing against, instead of my actual user.
Once I solved that matter, I discovered that the code is not working properly, this time the console says:
ssh2_scp_send(tmp/sshPm/prueba1.csv): failed to open stream: No such file or directory in /home/josemaria/Desktop/API UBcloud/CSV/provisioning.php on line 32
Here is my code:
function arrayToCsv($array, $filename, $delimiter = ';'){
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="'.$filename.'";');
$f = fopen('/tmp/sshPm/'.$filename, 'r');
foreach ($array as $line) {
fputcsv($f, $line, $delimiter);
}
}
function connectSsh($filename){
$sshConnection = ssh2_connect($host, $port);
if (!$sshConnection) {
die('Conexión Fallida');
}
ssh2_auth_pubkey_file($sshConnection, $user, $pubKey, $priKey);
ssh2_scp_send($sshConnection, "/tmp/sshPm/$filename", '/home/scripts/CSV', 0644);
$stream = ssh2_exec($sshConnection, "/home/scripts/sc_prov_ovpn_firm.sh $filename");
ssh2_exec($sshConnection, 'exit');
return $stream;
}
for ($i=0; $i < 10; $i++) {
$array[] = array(
"VAAAAAGH",
"THE WAGH IS HERE",
200 + $i,
564451 +$i,
"sip",
"",
"",
"",
"8.8.8.8",
"8.8.4.4",
20048,
"Modelo Terminal",
"677shdG3"
);
}
$filename = 'test.csv';
arrayToCsv($array, $filename);
$stream = connectSsh($filename);
print_r($stream);
As you can see, I intend the CSV's to be created and stored in /tmp. Even though the csv is created and placed in the right directory, whenever I reach scp_send, this method proves incapabe of finding it. I don't know if this could be related to the fact that I am using root to veify my public key as I've seen that it should be the user you are logged in with.
I also get the following warning right inmediately, but I guess this is consecuence of the first one... In any case, here it is:
PHP Warning: ssh2_scp_send(): Unable to read source file in /home/josemaria/Desktop/API UBcloud/CSV/provisioning.php on line 32.
I have tried using a wrapper instead of fopen() but with no success. As I said, this is the first time I work using ssh and PHP so I would ask you to explain a little bit at least!
Thank you so much for the help!
UPDATE
I managed to solve partially the issue by following ArSeN's advice and creating a directory in /Desktop and changing all the routes to that one, instead of /tmp. Now the problem I face is that I am not sure where to place the files once created. So my next question related to this issue would be:
Where should I store all the CSV's generated locally? As you see, I am doing it in /Documents since I have no restrictions there to read/modify, but I would say the answer lies in /, maybe /var? I really have no clue about much of this stuff...
Thank you again for the help provided!
This is what my code looks like now:
function arrayToCsv($array, $filename, $delimiter = ','){
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="'.$filename.'";');
$f = fopen("/home/josemaria/Documents/sshPm/$filename", 'w');
foreach ($array as $line) {
fputcsv($f, $line, $delimiter);
}
fclose($f);
}
function connectSsh($filename){
$sshConnection = ssh2_connect($host, $port);
if (!$sshConnection) {
die('Conexión Fallida');
}
ssh2_auth_pubkey_file($sshConnection, $user, $pubKey, $priKey);
ssh2_scp_send($sshConnection, "/home/josemaria/Documents/sshPm/$filename", "/home/scripts/CSV/$filename", 0644);
$stream = ssh2_exec($sshConnection, "/home/scripts/sc_prov_ovpn_firm.sh $filename");
ssh2_exec($sshConnection, 'exit');
return $stream;
}
for ($i=0; $i < 10; $i++) {
$array[] = array(
"VAAAAAGH",
"THE WAGH IS HERE",
200 + $i,
564451 +$i,
"sip",
"",
"",
"",
"8.8.8.8",
"8.8.4.4",
20048,
"Modelo Terminal",
"677shdG3"
);
}
$filename = 'test.csv';
arrayToCsv($array, $filename);
$stream = connectSsh($filename);
print_r($stream);
You should close the file handler so the file does actually get written and is not in some I/O buffer.
function arrayToCsv($array, $filename, $delimiter = ';'){
// all your existing code here ...
fclose($f);
}
Also with your copy target it seems like you are putting a file where a folder is before, meaning:
ssh2_scp_send($sshConnection, "/tmp/sshPm/$filename", '/home/scripts/CSV', 0644);
should probably be:
ssh2_scp_send($sshConnection, "/tmp/sshPm/$filename", "/home/scripts/CSV/$filename", 0644);
The answer to my issue had to do with permissions. I had some other errors as #ArSeN pointed out, but the warning I got and which prevented the code to work was because I was trying to store and read the files in /tmp withouth the permissions required to do so. So give yourself full permissions on the dir or change the dir for another. I still have some problems and issues with this code, but I do feel that they belong to a separate question that I will be linking here: How can I generate a CSV on Win and Linux using PHP and ProcessMaker? Which paths are reccommended to store the files locally?
This is a php script for a user login system that I am developing.
I need it to read from, and write to, the /students/students.txt file, but it won't even read the content already contained in the file.
<?php
//other code
echo "...";
setcookie("Student", $SID, time()+43200, "/");
fopen("/students/students.txt", "r");
$content = fread("/students/students.txt", filesize("/students/students.txt"));
echo $content;
fclose("/students/students.txt");
fopen("/students/students.txt", "w");
fwrite("/students/students.txt", $content."\n".$SID);
fclose("/students/students.txt");
//other code
?>
You are not using fopen() properly. The function returns a handle that you then use to read or edit the file, for example:
//reading a file
if ($handle = fopen("/students/students.txt", "r"))
{
echo "info obtained:<br>";
while (($buffer = fgets($handle))!==false)
{ echo $buffer;}
fclose($handle);
}
//writing/overwriting a file
if ($handle = fopen("/students/students.txt", "w"))
{
fwrite($handle, "hello/n");
fclose($handle);
}
Let me know if that worked for you.
P.S.: Ty to the commentators for the constructive feedback.
There are many ways to read/write to file as others have demonstrated. I just want to illustrate the mistake in your particular approach.
fread takes a file handle as param, NOT a string that represents the path to the file.
So your line:
$content = fread("/students/students.txt", filesize("/students/students.txt")); is incorrect.
It should be:
$file_handle = fopen("/students/students.txt", "r");
$content = fread($file_handle, filesize("/students/students.txt"));
Same thing when you write contents to file using fwrite. Its reference to the file is a File Handle opened using fopen NOT the filepath. when opening a file using fopen() you can also check if the $file_handle returned is a valid resource or is false. If false, it means the fopen operation was not successful.
So your code:
fopen("/students/students.txt", "w");
fwrite("/students/students.txt", $content."\n".$SID);
fclose("/students/students.txt");
Needs to be re-written as:
$file_handle = fopen("/students/students.txt", "w");
fwrite($file_handle, $content."\n".$SID);
fclose($file_handle);
You can see that fclose operates on file handles as well.
File Handle (as per php.net):
A file system pointer resource that is typically created using fopen().
Here are a couple of diagnostic functions that allow you to validate that a file exists and is readable. If it is a permission issue, it gives you the name of the user that needs permission.
function PrintMessage($text, $success = true)
{
print "$text";
if ($success)
print " [<font color=\"green\">Success</font>]<br />\n";
else
print(" [<font color=\"red\">Failure</font>]<br />\n");
}
function CheckReadable($filename)
{
if (realpath($filename) != "")
$filename = realpath($filename);
if (!file_exists($filename))
{
PrintMessage("'$filename' is missing or inaccessible by '" . get_current_user() . "'", false);
return false;
}
elseif (!is_readable($filename))
{
PrintMessage("'$filename' found but is not readable by '" . get_current_user() . "'", false);
return false;
}
else
PrintMessage("'$filename' found and is readable by '" . get_current_user() . "'", true);
return true;
}
I've re-written your code with (IMO) a cleaner and more efficient code:
<?php
$SID = "SOMETHING MYSTERIOUS";
setcookie("Student", $SID, time()+43200, "/");
$file = "/students/students.txt"; //is the full path correct?
$content = file_get_contents($file); //$content now contains /students/students.txt
$size = filesize($file); //do you still need this ?
echo $content;
file_put_contents($file, "\n".$SID, FILE_APPEND); //do you have write permissions ?
file_get_contents
file_get_contents() is the preferred way to read the contents of a
file into a string. It will use memory mapping techniques if supported
by your OS to enhance performance.
file_put_contents
This function is identical to calling fopen(), fwrite() and
fclose() successively to write data to a file. If filename does not
exist, the file is created. Otherwise, the existing file is
overwritten, unless the FILE_APPEND flag is set.
Notes:
Make sure the full path /students/students.txt is
correct.
Check if you've read/write permissions on /students/students.txt
Learn more about linux file/folder permissions or, if you don't access to the shell, how to change file or directory permissions via ftp
Try to do this:
fopen("students/students.txt", "r");
And check to permissions read the file.
I'm using php to access file by ftp.
My file read function is:
function _rfile ($file = null) {
if ( is_readable($file) ) {
if ( !($fh = fopen($file, 'r')) ) return false;
$data = fread($fh, filesize($file));
fclose($fh);
return $data;
}
return false;
}
I am getting an error on file close operation in this function with one of my ftp host.
error:
fclose(): FTP server error 550:550 The specified network name is no
longer available.
This function worked fine for some other ftp host's.
Could smb advice a solution?
It may due to the access permission settings.
Please look at this link. http://forums.iis.net/t/1107644.aspx
I have an issue I can't seem to find the solution for. I am trying to write to a flat text file. I have echoed all variables out on the screen, verified permissions for the user (www-data) and just for grins set everything in the whole folder to 777 - all to no avail. Worst part is I can call on the same function from another file and it writes. I can't see to find the common thread here.....
function ReplaceAreaInFile($AreaStart, $AreaEnd, $File, $ReplaceWith){
$FileContents = GetFileAsString($File);
$Section = GetAreaFromFile($AreaStart, $AreaEnd, $FileContents, TRUE);
if(isset($Section)){
$SectionTop = $AreaStart."\n";
$SectionTop .= $ReplaceWith;
$NewContents = str_replace($Section, $SectionTop, $FileContents);
if (!$Handle = fopen($File, 'w')) {
return "Cannot open file ($File)";
exit;
}/*
if(!flock($Handle, LOCK_EX | LOCK_NB)) {
echo 'Unable to obtain file lock';
exit(-1);
}*/
if (fwrite($Handle, $NewContents) === FALSE) {
return "Cannot write to file ($File)";
exit;
}else{
return $NewContents;
}
}else{
return "<p align=\"center\">There was an issue saving your settings. Please try again. If the issue persists contact your provider.</p>";
}
}
Try with...
$Handle = fopen($File, 'w');
if ($Handle === false) {
die("Cannot open file ($File)");
}
$written = fwrite($Handle, $NewContents);
if ($written === false) {
die("Invalid arguments - could not write to file ($File)");
}
if ((strlen($NewContents) > 0) && ($written < strlen($NewContents))) {
die("There was a problem writing to $File - $written chars written");
}
fclose($Handle);
echo "Wrote $written bytes to $File\n"; // or log to a file
return $NewContents;
and also check for any problems in the error log. There should be something, assuming you've enabled error logging.
You need to check for number of characters written since in PHP fwrite behaves like this:
After having problems with fwrite() returning 0 in cases where one
would fully expect a return value of false, I took a look at the
source code for php's fwrite() itself. The function will only return
false if you pass in invalid arguments. Any other error, just as a
broken pipe or closed connection, will result in a return value of
less than strlen($string), in most cases 0.
Also, note that you might be writing to a file, but to a different file that you're expecting to write. Absolute paths might help with tracking this.
The final solution I ended up using for this:
function ReplaceAreaInFile($AreaStart, $AreaEnd, $File, $ReplaceWith){
$FileContents = GetFileAsString($File);
$Section = GetAreaFromFile($AreaStart, $AreaEnd, $FileContents, TRUE);
if(isset($Section)){
$SectionTop = $AreaStart."\n";
$SectionTop .= $ReplaceWith;
$NewContents = str_replace($Section, $SectionTop, $FileContents);
return $NewContents;
}else{
return "<p align=\"center\">There was an issue saving your settings.</p>";
}
}
function WriteNewConfigToFile($File2WriteName, $ContentsForFile){
file_put_contents($File2WriteName, $ContentsForFile, LOCK_EX);
}
I did end up using absolute file paths and had to check the permissions on the files. I had to make sure the www-data user in Apache was able to write to the files and was also the user running the script.
I am using code that i found on here which works great but the moment i try to access a file in a sub-directory it just doesn't want to work.
It gets a file, creates a temp file to write to, then looks for some text in the file and replaces that text with new text, then saves the updated file, then deletes the temp file.
The below works fine:
$reading = fopen('links.htm', 'r');
$writing = fopen('links.tmp', 'w+');
$replaced = false;
while (!feof($reading)) {
$line = fgets($reading);
if (stristr($line,'::template::')) {
$line = "replacement line!\n";
$replaced = true;
}
fputs($writing, $line);
}
fclose($reading); fclose($writing);
// might as well not overwrite the file if we didn't replace anything
if ($replaced)
{
rename('links.tmp', 'links.htm');
} else {
unlink('links.tmp');
}
This doesnt work:
$reading = fopen('path/to/links.htm', 'r');
$writing = fopen('path/to/links.tmp', 'w+');
$replaced = false;
while (!feof($reading)) {
$line = fgets($reading);
if (stristr($line,'::template::')) {
$line = "replacement line!\n";
$replaced = true;
}
fputs($writing, $line);
}
fclose($reading); fclose($writing);
// might as well not overwrite the file if we didn't replace anything
if ($replaced)
{
rename('path/to/links.tmp', 'path/to/links.htm');
} else {
unlink('path/to/links.tmp');
}
Any suggestions?
The path is absolute i defined it in the code earlier and use it to create file and write files to but when i want the code above to work in the same way it just doesn't want to.
Also the folders permissions has been set to write and read which also works fine.
Ran the code in the sub folder and works fine but not from the top level directory.
Error reporting is turned off, turned it on now:
The process cannot access the file because it is being used by another process. (code: 32)
So after a week of looking around, testing, breaking and recoding a lot i found a simple way to do this.
Since most of the content is dynamically created as well as the links.htm file, it was quite hard finding where and when the file is accessed since the code accesses about 300 of my clients sites and updates the links file.
Simple fix:
$path = "path/on/server/";
$conn_id = ftp_connect("ftp.testftp.com") or die("Could not connect");
ftp_login($conn_id,"admin","ert456");
$old_file = $path.'links.htm';
$new_file = $path.'_template.htm';
ftp_rename($conn_id, $old_file, $new_file);
$source = fopen($new_file, "w");
$localTarget = "path/to/links.htm";
ftp_fget($conn,$source,$localTarget,FTP_ASCII);
$reading = fopen('path/to/links.htm', 'r');
$writing = fopen('path/to/_template.htm', 'w+');
$replaced = false;
while (!feof($reading)) {
$line = fgets($reading);
if (stristr($line,'::template::')) {
$line = "replacement line!\n";
$replaced = true;
}
fputs($writing, $line);
}
fclose($reading); fclose($writing);
// might as well not overwrite the file if we didn't replace anything
if ($replaced)
{
rename('path/to/_template.htm', 'path/to/links.htm');
} else {
unlink('path/to/_template.htm');
}
$local_file = "path/to/links.htm";
ftp_put($conn_id, $path, $local_file, FTP_BINARY);
ftp_close($conn_id);
There is probably easier ways to do this but this is what worked for me, hope it helps someone.
PHP will need to have write permissions to that directory in order to write to it. It may be that it has write permissions to the current directory ., but does not have permissions to write to the subdirectory ./path/to/.
Edit: If you get a PHP error, you should include it in your question.
Edit after OP Edit:
That error means that something currently has links.htm opened. I see that you're fclose()ing the file before renaming it, so my guess is that you probably have links.htm open in some other application (e.g. browser or text editor).
Edit #3:
If you do not have one of the links.htm or links.tmp files open in another application, it could be that you're using Windows - in which case the rename() call will execute before the fclose() even though it comes after it in the code. The solution to this is to add a sleep() call after closing the handle:
fclose($reading); fclose($writing);
sleep(1); // this should allow the handle to be properly closed before the rename() call