PHP How recursive directory scan with glob() can ruin your computer

As a developer, I also use XAMPP on one of my Windows machines to set up a quick dev server and testing environment. And during many coding sessions, you find yourself at various issues and mistakes that sometimes can cost you all your files. In this article I will describe exactly one (actually, two) such occasion(s) when I almost accidentally deleted entire WINDOWS C:\ drive.

Alternative Title: How PHP ‘s recursive files & directory removal function constructed with glob() almost ruined my computer twice!

PHP Logo

PHP Logo

Here is the php function for recursive files and directory removal I was indirectly using, while I was trying to improve some other related code:

<?php
// recursive directory delete
function recursiveDelete($dir) {
    $tree = glob(rtrim($dir, '/') . '/*');
    if (is_array($tree)) {
        foreach($tree as $file) {
            if (is_dir($file)) {
                recursiveDelete($file);
            } elseif (is_file($file)) {
                unlink($file);
            }
        }
    }
    rmdir($dir);
}
?>

WARNINGDO NOT use ABOVE unsafe version which will potentially end your world!

glob() will get a basic tree subdirectory structure of the passed $dir argument and from there recursively repeat itself untill it reaches all the way through the very last file.

For testing purposes and to examine/learn what could possibly go wrong with above function, instead of the actual “hot” code we will use a benign echo version below that will simply print absolute path of the currently scanned directory or a file:

To scan recursively entire $dir:

<?php
// recursive directory scan
function recursiveScan($dir) {
    $tree = glob(rtrim($dir, '/') . '/*');
    if (is_array($tree)) {
        foreach($tree as $file) {
            if (is_dir($file)) {
                echo $file . '<br/>';
                recursiveScan($file);
            } elseif (is_file($file)) {
                echo $file . '<br/>';
            }
        }
    }
}
?>

To scan only first level of $dir we need to remove inner recursion:

<?php
// directory scan
function Scan($dir) {
    $tree = glob(rtrim($dir, '/') . '/*');
    if (is_array($tree)) {
        foreach($tree as $file) {
            echo $file . '<br/>';
        }
    }
}
?>

Ok, so where is the problem you may ask? Well, if you work on some other piece of code that needs to call the above function AND at some point you *do not* supply $dir argument to the function for whatever reason (oops!) — instead of scanning desired directory only it will scan your entire drive from root!

Here is an example of non-recursive scan result:

/Documents and Settings
/IO.SYS
/MSDOS.SYS
/NTDETECT.COM
/Program Files
/RECYCLER
/System Volume Information
/WINDOWS
/boot.ini
/cmdcons
/cmldr
/ntldr
/xampp

As you can see above, the function scanned (read: set to delete) entire C:\ drive structure!! Oh, what a dear, dear mistake. The first directory on the line is the most sensitive one (as Murphy would like): user’s documents and application settings. Ouch!

Whoever in PHP team was responsible for glob() function probably thought that this was a cool idea. I wonder how many PHP developers were actually ruined by this due proper lack of experience!? :)

Just test it, simply set $dir to empty value or remove if from the function’s argument altogether and you will see massive process starting — scanning your entire C:\ drive (or wherever your XAMPP server resides).

With no argument supplied it will scan your root drive structure on which server resides:

<?php
// recursive directory scan
function recursiveScan() {
    $tree = glob(rtrim($dir, '/') . '/*');
    if (is_array($tree)) {
        foreach($tree as $file) {
            echo $file . '<br/>';
        }
    }
}
?>

SIMPLE FIX & PROTECTION

Now, how can we easily prevent this from ever happening in the future still using above code and glob() function? Simply adding another if() {} condition wrapper to check if $dir is defined and not empty:

Protect filesystem and prevent recursive directory delete if no argument is passed:

<?php
// recursive directory delete
function recursiveDeleteProtected($dir) {
    if($dir != '') {
        // ...original function code...
    }
}
?>

In newer Windows OS versions, administrative rights & privileges were reinforced, so it is possible that in such cases (I am not willing to test it though, LOL) the harm would be restricted and contained to non-protected and user directories only (e.g. system directories would be left alone). But, better be extra safe, then sorry.

C:\ FILESYSTEM RECOVERY AFTER A DISASTER

So, how did I recover from the above disaster? Well, when it first happened to me last year, I didn’t. Simply, had fresh ghost backup of my entire system drive and restored it. No harm done.

Second time was yesterday and I noticed hard-drive activity in time on my laptop, so less than 2 GB of data was deleted by then and immediately realized what was going on. Brought up XAMPP’s Control Panel icon and as fast as possible stopped Apache server from running.

After that, half of my desktop icons already disappeared and some damage was done. PHP is very fast for performing this type of tasks, because it bypasses OS level and RECYCLE BIN functions. It deletes all files DIRECTLY without any confirmations whatsoever.

And, that is one LUCKY thing, as a matter of fact! I always keep some tool for accidental files recovery, and for few years back I have settled down to PC Tools (by Symantec). It saved me countless number of times already. It is not perfect, but it is almost there.

Now, if you happen to be in the same situation as myself, and you don’t have installed one such tool already, assuming your Windows functions were saved, you may install or use portable version of something similar.

For me, I was very lucky, since the executable file and folder of PC Tools .exe was still intact, and I could immediately start the recovery procedure. I always choose Deep or Physical scan, not sure about the difference, they return same scan results.

Once the scanning procedure is over, you have to restore all your C:\ drive structure *except* RECYCLE BIN and ERASED folders. Why? As previously said, PHP does NOT delete your files into Window’s Recycle Bin. It removes them directly, instead. So, good news is — your current files are in actual directories that you need to recover (Documents/Users/Windows…).

PC Tools File Recoverer

PC Tools File Recoverer

Remember, use different partition to recover deleted files and folders from recovery tool, USB flash drive or external hard drive. Do not recover them DIRECTLY to C:\ drive!! It will overwrite not-yet-recovered files and dirs in such a way. Be careful and follow recovery program’s instructions.

Once you restored your lost C:\ system files to another drive use CUT/PASTE (or COPY/PASTE) to transfer them back to their original location (this is easy).

CONCLUSION

In the end, I was able to recover over 99% of the apps and settings. Some website projects under XAMPP htdocs public directory were fully restored, Git for Windows was badly damaged and a forced re-install did not help. Luckily enough, after proper removal via Windows Control Panel and fresh installation, all projects and SSH keys were recognized (good thing is I do not hold git projects on my C:\ drive anyway) and few things like missing file-app associations. Nothing of a major concern. Oh, and my desktop icons, which was easy to restore. This is one of the reasons why I prefer SPL iterator over glob() and custom recursive scan functions, if you forget to pass directory name, it will simply not work.

What an adventure it was!

 

Comments


Post A Comment

I have read and consent to Privacy Policy and Terms and Conditions