1 /** 2 * File Tree Walking EntropySource 3 * 4 * Copyright: 5 * (C) 1999-2008 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.entropy.proc_walk; 12 13 import botan.constants; 14 version(Posix): 15 static if (BOTAN_HAS_ENTROPY_SRC_PROC_WALKER): 16 17 import botan.entropy.entropy_src; 18 import memutils.vector; 19 import botan.utils.types; 20 import core.stdc.string; 21 import core.stdc.config; 22 import core.sys.posix.sys.types; 23 import core.sys.posix.sys.stat; 24 import core.sys.posix.fcntl; 25 import core.sys.posix.unistd; 26 import core.sys.posix.dirent; 27 import std.string : toStringz; 28 import std.array; 29 30 final class DirectoryWalker : FileDescriptorSource 31 { 32 public: 33 this(in string root) 34 { 35 m_cur_dir = makePair!(DIR*, string)(null, ""); 36 if (DIR* root_dir = opendir(root.toStringz)) 37 m_cur_dir = makePair(root_dir, root); 38 } 39 40 ~this() 41 { 42 if (m_cur_dir.first) 43 closedir(m_cur_dir.first); 44 } 45 46 override int nextFd() 47 { 48 while (true) 49 { 50 Pair!(dirent*, string) entry = getNextDirent(); 51 52 if (!entry.first) 53 break; // no more dirs 54 55 const string filename = cast(string) entry.first.d_name[0 .. strlen(entry.first.d_name.ptr)]; 56 57 if (filename == "." || filename == "..") 58 continue; 59 60 const string full_path = entry.second ~ '/' ~ filename; 61 62 stat_t stat_buf; 63 if (.lstat(full_path.toStringz, &stat_buf) == -1) 64 continue; 65 66 if (S_ISDIR(stat_buf.st_mode)) 67 { 68 addDirectory(full_path); 69 } 70 else if (S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH)) 71 { 72 int fd = .open(full_path.toStringz, O_RDONLY | O_NOCTTY); 73 74 if (fd > 0) 75 return fd; 76 } 77 } 78 79 return -1; 80 } 81 82 private: 83 void addDirectory(in string dirname) 84 { 85 m_dirlist.insertBack(dirname); 86 } 87 88 Pair!(dirent*, string) getNextDirent() 89 { 90 while (m_cur_dir.first) 91 { 92 if (dirent* dir = readdir(m_cur_dir.first)) 93 return makePair(dir, m_cur_dir.second); 94 95 closedir(m_cur_dir.first); 96 m_cur_dir = makePair!(DIR*, string)(null, ""); 97 98 while (!m_dirlist.empty && !m_cur_dir.first) 99 { 100 const string next_dir_name = m_dirlist.front; 101 m_dirlist = Vector!string(m_dirlist[1 .. $]); 102 103 if (DIR* next_dir = opendir(next_dir_name.toStringz)) 104 m_cur_dir = makePair(next_dir, next_dir_name); 105 } 106 } 107 108 return Pair!(dirent*, string)(); // nothing left 109 } 110 111 Pair!(DIR*, string) m_cur_dir; 112 Vector!string m_dirlist; 113 } 114 115 116 interface FileDescriptorSource 117 { 118 public: 119 abstract int nextFd(); 120 121 } 122 123 /** 124 * File Tree Walking Entropy Source 125 */ 126 final class ProcWalkingEntropySource : EntropySource 127 { 128 public: 129 @property string name() const { return "Proc Walker"; } 130 131 void poll(ref EntropyAccumulator accum) 132 { 133 __gshared immutable size_t MAX_FILES_READ_PER_POLL = 2048; 134 const double ENTROPY_ESTIMATE = 1.0 / (8*1024); 135 136 if (!m_dir) 137 m_dir = new DirectoryWalker(m_path); 138 139 SecureVector!ubyte* io_buffer = &accum.getIoBuffer(4096); 140 foreach (size_t i; 0 .. MAX_FILES_READ_PER_POLL) 141 { 142 int fd = m_dir.nextFd(); 143 144 // If we've exhaused this walk of the directory, halt the poll 145 if (fd == -1) 146 { 147 destroy(m_dir); 148 m_dir = null; 149 break; 150 } 151 152 ssize_t got = .read(fd, io_buffer.ptr, 4096); 153 close(fd); 154 155 if (got > 0) 156 accum.add(io_buffer.ptr, got, ENTROPY_ESTIMATE); 157 158 if (accum.pollingGoalAchieved()) 159 break; 160 } 161 } 162 163 this(in string root_dir) 164 { 165 m_path = root_dir; 166 } 167 168 ~this() { if (m_dir) destroy(m_dir); } 169 170 private: 171 const string m_path; 172 FileDescriptorSource m_dir; 173 } 174 175 @nogc nothrow pure private: 176 bool S_ISTYPE( mode_t mode, uint mask ) { return ( mode & S_IFMT ) == mask; } 177 bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } 178 bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); }