Passed
Push — master ( 9ae471...0356cb )
by Tony Karavasilev (Тони
19:47
created

FileShredder::fileShredding()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 24
ccs 9
cts 9
cp 1
rs 9.2222
cc 6
nc 10
nop 1
crap 6
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
 * @mixin ValidateFileNames
21
 */
22
class FileShredder extends RandomnessContainer implements SecureFileErasure
23
{
24
    /**
25
     * File name and path validations.
26
     *
27
     * {@internal Reusable implementation of the common file name validation. }}
28
     */
29
    use ValidateFileNames;
30
31
    /**
32
     * Internal method for calculating the file size in bytes.
33
     *
34
     * @param string $filename The filename and location.
35
     *
36
     * @return int The file size in bytes.
37
     * @throws \Exception Validation errors.
38
     */
39 4
    protected function calculateFileSize($filename)
40
    {
41 4
        $sizeInBytes = filesize($filename);
42
43
        // @codeCoverageIgnoreStart
44
        if ($sizeInBytes === false) {
45
            throw new \RuntimeException('The operating system could not obtain the correct size of the file.');
46
        }
47
        // @codeCoverageIgnoreEnd
48
49 4
        return $sizeInBytes;
50
    }
51
52
    /**
53
     * Internal method for writing of pseudo-random content to a file.
54
     *
55
     * @param string $filename The filename and location.
56
     * @param int $pass The current iteration pass logic.
57
     * @param int $flag The file handle lock flag.
58
     *
59
     * @return bool The write operation result.
60
     * @throws \Exception Validation or filesystem errors.
61
     */
62 2
    protected function writeContentToFile($filename, $pass, $flag)
63
    {
64 2
        $written = false;
65
66 2
        if ($pass === 1) {
67 2
            $written = file_put_contents($filename, "\x0", $flag);
68 2
        } elseif ($pass === 2) {
69 2
            $written = file_put_contents($filename, "\x1", $flag);
70 2
        } elseif ($pass === 3) {
71 2
            $written = file_put_contents($filename, $this->randomnessSource->getBytes(1), $flag);
72
        }
73
74 2
        return is_int($written);
75
    }
76
77
    /**
78
     * Internal method for shredding the physical file based on the DOD 5220.22-M (3 passes) standard.
79
     *
80
     * @param string $filename The filename and location.
81
     *
82
     * @return bool The file shredding operation result.
83
     * @throws \Exception Validation or filesystem errors.
84
     */
85 4
    protected function fileShredding($filename)
86
    {
87 4
        $sizeInBytes = $this->calculateFileSize($filename);
88
89 4
        for ($pass = 1; $pass <= 3; $pass++) {
90 4
            for ($i = 0; $i < $sizeInBytes; $i++) {
91 2
                $flag = ($i === 0) ? LOCK_EX : (FILE_APPEND | LOCK_EX);
92
93 2
                $written = $this->writeContentToFile($filename, $pass, $flag);
94
95
                // @codeCoverageIgnoreStart
96
                if ($written === false) {
97
                    throw new \RuntimeException('Problem with writing to the current filesystem.');
98
                }
99
                // @codeCoverageIgnoreEnd
100
            }
101
102
            // Sleep for 15 milliseconds for the disk verify the written data and sync buffers
103 4
            usleep(15000);
104
        }
105
106 4
        $written = file_put_contents($filename, '', LOCK_EX);
107
108 4
        return (unlink($filename) === true && $written !== false);
109
    }
110
111
    /**
112
     * Data erasure and wiping of a file.
113
     *
114
     * @param string $filename The filename and location.
115
     *
116
     * @throws \Exception Validation errors.
117
     *
118
     * @internal Files that are bigger than 2 GB may not be deletable or behave irrationally for x86 platforms.
119
     */
120 8
    public function eraseFile($filename)
121
    {
122 8
        if (!is_string($filename)) {
123 2
            throw new \InvalidArgumentException('The file path must be of type string.');
124
        }
125
126 6
        $this->validateFileNamePath($filename);
127
128
        // @codeCoverageIgnoreStart
129
        if ($this->fileShredding($filename) === false) {
130
            throw new \RuntimeException('Failed to delete the file from the filesystem.');
131
        }
132
        // @codeCoverageIgnoreEnd
133 4
    }
134
}
135