I found this simple function that simply clears the contents of a file:
function createEmptyFile($filename)
{
//
$fp = fopen($filename, 'w');
//
fclose($fp);
}
For example, resets the log, or something else.
But I have a question whether this function is really 100% safe, that is, its behavior is uniquely determined, and the result will be as expected.
I have a strange feeling that there is a possibility that the file: may not be cleaned, or may be damaged, or filled with random data in some case.
I understand that perhaps this is all prejudice, but I would like to know exactly how right it is. In particular, I am confused by the absence of any command between opening and closing the handle.
For example, I assume that if the PHP interpreter will perform optimization, then due to the lack of a command, between opening and closing the handle, it may skip this block, or something like that.
Sorry if I create a panic in vain.
I am confused by the absence of any command between opening and closing the handle.
Let me put something to the front:
The left '{' and the right '}' bracket are flow-controls and need commands in between. fopen() and fclose() are not. Though these are only 2 commands they inherit many more.
That said lets look into the tasks of them: fopen() does ...
(a) look for the specific file,
(b) if the file is not existing it creates the file
(c) opens the file for writing only,
(d) redirects the file-pointer to the beginning of the file,
(e) if the file is longer then zero it will truncate it to zero length.
In particular, I am confused by the absence of any command between opening and closing the handle.
You see there are many "commands" in between. So don't worry.
I have a strange feeling that there is a possibility that the file: may not be cleaned
To be exact, the file is not cleared, it is truncated to zero length. The earlier data of this file is still in the memory of your data-storage. But this is subject to your operation-system. There exist programs to delete these data-blocks entirely.
[...], or may be damaged
I don't understand the question. You are going to delete a file - what more damage do you expect?
[...], or filled with random data in some case.
That is C-style for creating variables by setting up a pointer to free memory but not clearing the data earlier was in it and giving this duty to you. But here it is just a truncate and not a redirecting of the file pointer. This fear could be ignored.
But I have a question whether this function is really 100% safe, that is, its behavior is uniquely determined, and the result will be as expected.
Yes, normally the behavior is uniquely determined. But you have to expect some side-effects:
if the file does not exist it will be created. You need to have write-access to the directory. (else it will come back with false)
if you have no write-access to the file it will come back with false.
if your php-environment uses "safe mode" the possible difference of owner and user leads to a fail of fopen(). You need to be sure that the file is worked only by you.
It can happen that you just write fopen() and don't check the return-parameter. That may cause a problem and lead to: not 100% safe if you don't react correctly.
So - yes fopen() and fclose() are sufficient, correct and inform you if the work is not done properly.
Related
I am writing some json results in files in PHP on shared hosting (fwrite).
Then I read those files to extract json results (file_get_contents).
It happens some times (maybe one out of more than one thousand) that when I read this file it appears truncated: I can only read a multiple of the first 32768 bytes of the file.
I added some code to copy/paste the file I am reading in case the json string is not valid, and I then get 2 different files: the original one was correctly written as it contains a valid json string and the copied one contains only the beginning of the original one and has a size of x*32768 bytes.
Would you have any idea of what could be the problem and how to solve this? (I don't know how to investigate further)
Thank you
Without example code it is impossible to give a 'fix my code' answer, but when doing file write/read sort of programming, you should follow a simple process (which, from the description, is missing one fairly critical step!)
First, write to a TEMP file (you are writing to a file, but it is important here to write to a TEMP file - otherwise, you could have race conditions....... ;);
an easy way to do that in php
$yourData = "whateverYourDataIs....";
$goodfilename = 'whateverYourGoodFileNameIsForYourData.json';
$tempfilename = 'tempfile' . time(); // MANY ways to do this (lots of SO posts on it - just get a unique name every time you write ('unique' may not be needed if you only occasionally do a write, but it is a good safety measure to avoid collisions and time() works for many programs.)
// Now, use $tempfilename in your fwrite.
$fwrite = fwrite($tempfilename,$yourData);
if ($fwrite === false) {
// the write failed, so do whatever 'error' function you may need
// since it failed, there should be no file, but not a bad idea to attempt to delete it
unlink ($tempfile);
}
else {
// the write succeeded, so let's do a 'sanity check' on the file to make sure it is good JSON (this is a 'paranoid' check, but "better safe than sorry", right?)
if(json_decode($tempfile)){
// we know the file is good JSON, so now RENAME (this is really fast, so collisions are almost impossible) NOTE: see http://php.net/manual/en/function.rename.php comments for some potential challenges and workarounds if you have trouble with rename.
rename($tempfilename,$goodfilename);
}
// Now, the GOOD file will contain your new data - and those read issues are gone! (though never say 'never' - it may be possible, but very unlikely!)
}
This may/not be your issue directly and you will have to suit this to fit your code, but as a safety factor - and a good way to avoid collisions, it should give you ~100% read success, which I believe is what you are after!)
If this doesn't help, then some direct code will be needed to provide a more complete answer.
As suggested by #UlrichEckhardt comment, it was due to read / write concurrency problem. I was trying to read a file that was being writen. I solved this by just waiting before trying to read the file again
Suddenly, an application isn't any longer able to output ZIP files. An inspection revealed the cause: The first character of the ZIP is a blank, which breaks the ZIP format spec.
To track down this problem, I enabled CStatementTracer, which prints each line of executed code to a log file. Didn't help. [Remark: declare(ticks=1); doesn't seem to trap each line of executed code]
I then set an output handler like so:
function callback( $buffer ) {
$deb = print_r( debug_backtrace(), TRUE );
file_put_contents( './statementTrager.log', $deb );
return $buffer;
}
ob_start("callback", 1 );
Unfortunately, this handler isn't called at all.
Q: Does a generic / canonical solution exists, which identifies the file / line of PHP-code, which emits the first character.
A solution, that finds the loc whatever other code gets executed.
Remarks:
Not even a single PHP file is closed using ?>
Meanwhile I found the suspicious like of code: A blank in front of a starting
Still, I'd like to get hints regarding a programmatic solution. Preferrably a solution written in pure PHP.
https://linux.die.net/man/1/strace is probably the most reliable tool to find out where the output comes from. Assuming you are on Linux. There must be similar tools for other platforms.
Although it will not give you the line of the php code, you can analyse the context of system calls made before and after the offensive character was sent. Usually it is enough to identify where the problem originates.
It is quite time consuming process though. Should be used as the last resort.
I want to delete a file by using PHP. I have used the unlink() function, but I was wondering about the security of unlink. Is the file completely deleted from the server? I want to make sure that there is no way to get the file back and the file is completely removed from the server.
open the file in binary mode for writing, write 1's over the entire file, close the file, and then unlink it. overwrites any data within the file so it cannot be recovered.
Personally i would say use 1's instead of 0's as 1's are actual data and will always write, where as 0's may not write, depending on several factors.
Edit: After some thought, and reading of comments, i would go with a hybrid approach, depending on "how deleted" you want the file to be, if you simply wish to make it so the data cannot be recovered, overwrite the entire files length with 1's as this is fast, and destroys the data, the problem with this, is it leaves a set length of uniform data on the disk which infers a file USED to be there and gives away the files length, giving vital pieces of forensic information. Simply writing random data will not avoid this also, as if all the drive sectors around this file are untouched, this will also leave a forensic trace.
The best solution factoring in forensic deletion, obfuscation and plausible deniability (again, this is overkill, but im adding it for the sake of adding it), overwrite the entire length of the file with 1's and then, for HALF the length of the file in bytes, write from mt_rand in random length sizes, from random starting points, leaving the impression that many files of varying lengths used to be in this area, thus creating a false trail. (again, this is completely overkill and is generally only needed by serial killers and the CIA, but im adding it for the sake of doing so).
the US government used to recommend a seven step wipe, for disks.
1) all '1's
2) all '0's
3) the pattern '01'
4) the pattern '10'
5) a random pattern
6) all '1'
7) a random pattern,
re the code sample, using a language like PHP is wrong for this type of wipe as your relaying on the OS really wipeing the file and not doing something cleaver like only wipeing it the last time or just unlinking it, however...
(untested)
$filename = "/usr/local/something.txt";
$size = filesize($filename);
$pat1 = chr(0);
$pat2 = chr(255);
$pat3 = chr(170);
$pat4 = chr(85);
$mask = str_repeat($pat1, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat2, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat3, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat4, $size);
file_put_contents($filename, $mask);
This might not answer HOW to perfectly delete a file "with PHP", but it answers your question: "Is the file completely deleted from the server ?"
In some cases, No! (on UNIX/POSIX OS).
According to the highest voted comment on the offical PHP unlink() manual page, the unlink function does not really delete the file, it's deleting the system link to the file's content ! As files can have several files names (!) [symlinks?] the file will only be deleted when ALL file names are unlinked. So, if your file has 2 names, then unlink() will not really delete the file unless you unlink() both file names. Dear linux guys, please correct me here if necessary.
This might be why the function is called unLINK() and not delete() !!!
Here a full quote of the excellent comment:
Deleted a large file but seeing no increase in free space or decrease of disk usage? Using UNIX or other POSIX OS? The unlink() is not about removing file, it's about removing a file name. The manpage says: `unlink - delete a name and possibly the file it refers to''. Most of the time a file has just one name -- removing it will also remove (free, deallocate) thebody' of file (with one caveat, see below). That's the simple, usual case.
However, it's perfectly fine for a file to have several names (see the link() function), in the same or different directories. All the names will refer to the file body and keep it alive', so to say. Only when all the names are removed, the body of file actually is freed. The caveat: A file's body may *also* bekept alive' (still using diskspace) by a process holding the file open. The body will not be deallocated (will not free disk space) as long as the process holds it open. In fact, there's a fancy way of resurrecting a file removed by a mistake but still held open by a process...
Have a look on unlink()'s sister function link() here.
The (imo) best way to delete a file via PHP:
The way to go to really delete a file with PHP (in linux) is to use the exec() function, which executes real bash commands (doing things with linux bash feel correct btw). In this case, the file test.jpg would be deleted by doing:
exec("rm test.jpg);
More info on how to use rm (remove) correctly can be found for example here. Please note: PHP needs the right to delete the file!
UPDATE: Unfortunatly, the linux rm command ALSO does not really delete the file if it has two names/links. Look here for more info.
I'll have a deeper research on that and give feedback...
It is possible that because of some fragmentation on the disk some parts of file will stay, even if the file is totally overwritten.
The other way is to run (by shell_exec()) external program, that is system specific. Here is an example (for Windows), however I have not tested it.
You should do multiple passes of overwriting to deminish traces. For instance using the US DoD 5220-22.M : "Overwrite all addressable locations with a character, its complement, then a random character and verify" (from killdisk site)
Here's what the EFF recommends to permanently remove a file http://ssd.eff.org/tech/deletion.
In my embedded Ubuntu device, I use: echo exec('rm /usr/share/subdirectory/subdirectory/filename'); This works for me.
if you use rm -f (--force) then linux will
ignore nonexistent files and arguments, never prompt
rm -d will
remove empty directories
If you enter rm --help at the prompt you get the help screen. The last lines read:
Note that if you use rm to remove a file, it might be possible to recover some of its contents, given sufficient expertise and/or time. For greater assurance that the contents are truly unrecoverable, consider using shred.
Since my system is a "closed" system then I'm not concerned about violating security issues. My logic being that one must have the system password to SSH into the OS and the only user interface is via web pages.
#Sliq's comments are still true to date. You need to decide for your case.
I was running over a sample script and hit on this particular issue.
The script starts off by setting
$docroot=$_SERVER['DOCUMENT_ROOT'];
For writing to a file,
#$fp = fopen("$docroot/../parts/partsorders.txt",'ab');
is used. But no matter what, this fails to write to the file.
After a tinkering with it for a while, I set the command to
#$fp = fopen('$docroot/../parts/partsorders.txt','ab');
used single quotes instead of double quotes and it worked fine!
My question is, isn't the former double quoted format supposed to work instead of the single quotes. What is happening here ?
Here is the stripped down code, guys (Assume that the file exists in the server) :
<?php
$docroot=$_SERVER['DOCUMENT_ROOT'];
$outputstring = "herpderp";
?>
<html>
<head>
<title>Quotes</title>
</head>
<body>
<?php
#$fp=fopen("$docroot/../parts/partsorders.txt","ab");
flock($fp, LOCK_EX);
if(!$fp) {
echo "<p><strong>Failed.</strong></p></body></html>";
exit;
}
fwrite($fp,$outputstring,strlen($outputstring));
flock($fp,LOCK_UN);
fclose($fp);
echo "<p>Order written.</p>";
?>
</body>
</html>
There are differences between single and double quoted strings in PHP. Single quoted strings do not cause the expansion of variable names while double quoted strings do. See here. Why your code works when you use the variable name with single quoted strings doesn't make sense to me. Furthermore, it's a bad idea to use # in front of your fopen commands, it will prevent you from seeing the error output.
The double quote one is the one that should evaluate $docroot for you. What the single quote should do is try to open a file that actually has $docroot as a string in it's path. Does
#$fp = fopen($docroot . '/../parts/partsorders.txt','ab');
yield the same result?
And do you use #to supress errors? In that case it should be before the function I think
$fp = #fopen($docroot . '/../parts/partsorders.txt','ab');
But don't do that when trying to find errors in your application. The thing is that you could very well get an error that it can't find the file and you just don't notice.
UPDATE
Thanks to pinkgothic for his comment, firstly for providing the correct terminology and secondly for pointing out that this answer is wrong.
I have experimented with the theory that shell is trying to expand the environment variable $docroot and found that shell expansion is not possible in fopen().
I tried the following code and got this error (I have a file called test.txt in my home directory)
$fp = fopen( '$HOME/test.txt', 'rb' );
//PHP Warning: fopen($HOME/test.txt): failed to open stream: No such file or directory in php shell code on line 1
So unless the OP has some configuration which allows shell expansion in fopen(), my answer is, as I say, incorrect.
The string $docroot/../parts/partsorders.txt is being sent directly to the operating system shell. As there is no $docroot variable set it is replaced by an empty string, so it is the same as using
#$fp = fopen('/../parts/partsorders.txt','ab');
Which is an absolute path starting from /, the document root.
#run from shell (bash)
~$ echo $docroot/../parts/partsorders.txt
/../parts/partsorders.txt
After my comment, I had a night's sleep and realised fopen() may not actually use/be like realpath() (which expects all segments to be set, even the ones that are irrelevant for the final normalised path).
It doesn't.
Accordingly, the reason your file is opened is actually fairly simple:
'$docroot/../parts/partsorders.txt'
...which is read as "pastorders.txt in the 'parts' folder which is a folder found in the folder above the '$docroot' folder which is in the current working directory" collapses to...
'parts/partsorders.txt'
...because fopen() simply vanishes $docroot/.. away without checking that $docroot exists. (Functions like realpath() do check it, which was throwing me off.)
So, your file is actually in <current working directory>/parts/partsorders.txt. (Since you're opening the file with the a flag, if it didn't exist there before, it was definitely created there.)
Whatever your $_SERVER['DOCUMENT_ROOT'] contains, it seems it's not what you want. Additionally, in some setups, you can't reasonably .. above $_SERVER['DOCUMENT_ROOT'] - permissions actually won't let you.
If that environment variable outright isn't set (if that's even possible; but I think this demonstrates the problem even if it isn't), the path is quite different:
"$docroot/../parts/partsorders.txt"
...becomes:
"/../parts/partsorders.txt"
...which tries to get up the hierarchy past the root point (/), which would of course not work.
I suggest echoing out or logging $_SERVER['DOCUMENT_ROOT'] and taking a look at what it actually contains, and if it's what you expect it to be.
What might be worth looking for is __DIR__ (or in older PHP versions, dirname(__FILE__)), which takes the directory the file. As long as the file knows where it is, you can just read out files relative to its location.
Sorry for my bad English.
I must to check 2 csv files, if strings with one id is different, must write to file.
If there is no string with id from 1st file in second file, must write this to file too.
it works, but with element (id=47) i have got a trouble. it into to files, but script sad, that there is only in one.
download script you can from here
http://sil-design.ru/uploads/script.zip
If you do a echo $str1[0].' - '.$str2[0].'<br />'; you will see that the two 47's are never compared. Also I am not sure what the t is in: $f2 = fopen($fileurl, 'rt');.
If you open your backup.csv in notepad and place your cursor after the 47;XL and hold delete to delete anything after it and save. Then try your script again, it should work. It seems that the backup.csv was created in a weird way, I am guessing PHP is getting an EOF before the file has even ended!