Completed
Push — master ( 55009c...687aa4 )
by Tony Karavasilev (Тони
19:52
created

FileShredder::eraseFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 11
ccs 4
cts 4
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
/**
4
 * Utility class for secure file erasure.
5
 */
6
7
namespace CryptoManana\Utilities;
8
9
use \CryptoManana\Core\Abstractions\Containers\AbstractRandomnessInjectable as RandomnessContainer;
10
use \CryptoManana\Core\Interfaces\Randomness\FileErasureInterface as SecureFileErasure;
11
use \CryptoManana\Core\Traits\CommonValidations\FileNameValidationTrait as ValidateFileNames;
12
13
/**
14
 * Class FileShredder - Utility class for file shredding.
15
 *
16
 * @package CryptoManana\Utilities
17
 *
18
 * @property \CryptoManana\Core\Abstractions\Randomness\AbstractGenerator $randomnessSource The randomness generator.
19
 */
20
class FileShredder extends RandomnessContainer implements SecureFileErasure
21
{
22
    /**
23
     * File name and path validations.
24
     *
25
     * {@internal Reusable implementation of the common file name validation. }}
26
     */
27
    use ValidateFileNames;
28
29
    /**
30
     * Internal method for calculating the file size in bytes.
31
     *
32
     * @param string $filename The filename and location.
33
     *
34
     * @return int The file size in bytes.
35
     * @throws \Exception Validation errors.
36
     */
37 2
    protected function calculateFileSize($filename)
38
    {
39 2
        $sizeInBytes = filesize($filename);
40
41
        // @codeCoverageIgnoreStart
42
        if ($sizeInBytes === false) {
43
            throw new \RuntimeException('The operating system could not obtain the correct size of the file.');
44
        }
45
        // @codeCoverageIgnoreEnd
46
47 2
        return $sizeInBytes;
48
    }
49
50
    /**
51
     * Internal method for shredding the physical file based on the DOD 5220.22-M (3 passes) standard.
52
     *
53
     * @param string $filename The filename and location.
54
     *
55
     * @return bool The file shredding operation result.
56
     * @throws \Exception Validation or filesystem errors.
57
     */
58 2
    protected function fileShredding($filename)
59
    {
60 2
        $sizeInBytes = $this->calculateFileSize($filename);
61
62 2
        $written = true;
63
64 2
        for ($i = 1; $i <= 3; $i++) {
65 2
            for ($j = 0; $j < $sizeInBytes; $j++) {
66 2
                $flag = ($j === 0) ? LOCK_EX : (FILE_APPEND | LOCK_EX);
67
68 2
                if ($i === 1) {
69 2
                    $written = file_put_contents($filename, "\x0", $flag);
70 2
                } elseif ($i === 2) {
71 2
                    $written = file_put_contents($filename, "\x1", $flag);
72 2
                } elseif ($i === 3) {
73 2
                    $written = file_put_contents($filename, $this->randomnessSource->getBytes(1), $flag);
74
                }
75
76
                // @codeCoverageIgnoreStart
77
                if ($written === false) {
78
                    throw new \RuntimeException('Problem with writing to the filesystem');
79
                }
80
                // @codeCoverageIgnoreEnd
81
            }
82
83
            // Sleep for 15 milliseconds for the disk verify the written data and sync buffers
84 2
            usleep(15000);
85
        }
86
87 2
        $written = file_put_contents($filename, '', LOCK_EX);
88
89 2
        return (unlink($filename) === true && $written !== false);
90
    }
91
92
    /**
93
     * Data erasure and wiping of a file.
94
     *
95
     * @param string $filename The filename and location.
96
     *
97
     * @throws \Exception Validation errors.
98
     *
99
     * @internal Files that are bigger than 2 GB may not be deletable or behave irrationally for x86 platforms.
100
     */
101 6
    public function eraseFile($filename)
102
    {
103 6
        if (!is_string($filename)) {
104 2
            throw new \InvalidArgumentException('The file path must be of type string.');
105
        }
106
107 4
        $this->validateFileNamePath($filename);
108
109
        // @codeCoverageIgnoreStart
110
        if ($this->fileShredding($filename) === false) {
111
            throw new \RuntimeException('Failed to delete the file from the filesystem.');
112
        }
113
        // @codeCoverageIgnoreEnd
114 2
    }
115
}
116