/* * FTW EntropySource * (C) 1999-2008 Jack Lloyd * * Distributed under the terms of the Botan license */ #include #include #include #include #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309 #endif #include #include #include #include #include namespace Botan { /** * Returns file descriptors. Until it doesn't */ class File_Descriptor_Source { public: /** * @return next file descriptor, or -1 if done */ virtual int next_fd() = 0; virtual ~File_Descriptor_Source() {} }; namespace { class Directory_Walker : public File_Descriptor_Source { public: Directory_Walker(const std::string& root) { add_directory(root); } ~Directory_Walker(); int next_fd(); private: void add_directory(const std::string&); std::deque > dirs; }; void Directory_Walker::add_directory(const std::string& dirname) { DIR* dir = ::opendir(dirname.c_str()); if(dir) dirs.push_back(std::make_pair(dir, dirname)); } Directory_Walker::~Directory_Walker() { while(dirs.size()) { ::closedir(dirs[0].first); dirs.pop_front(); } } int Directory_Walker::next_fd() { while(dirs.size()) { std::pair dirinfo = dirs[0]; struct dirent* entry = ::readdir(dirinfo.first); if(!entry) { ::closedir(dirinfo.first); dirs.pop_front(); continue; } const std::string filename = entry->d_name; if(filename == "." || filename == "..") continue; const std::string full_path = dirinfo.second + '/' + filename; struct stat stat_buf; if(::lstat(full_path.c_str(), &stat_buf) == -1) continue; if(S_ISDIR(stat_buf.st_mode)) add_directory(full_path); else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH)) { int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY); if(fd > 0) return fd; } } return -1; } } /** * FTW_EntropySource Constructor */ FTW_EntropySource::FTW_EntropySource(const std::string& p) : path(p) { dir = 0; } /** * FTW_EntropySource Destructor */ FTW_EntropySource::~FTW_EntropySource() { delete dir; } void FTW_EntropySource::poll(Entropy_Accumulator& accum) { const size_t MAX_FILES_READ_PER_POLL = 1024; if(!dir) dir = new Directory_Walker(path); MemoryRegion& io_buffer = accum.get_io_buffer(128); for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i) { int fd = dir->next_fd(); // If we've exhaused this walk of the directory, halt the poll if(fd == -1) { delete dir; dir = 0; break; } ssize_t got = ::read(fd, &io_buffer[0], io_buffer.size()); ::close(fd); if(got > 0) accum.add(&io_buffer[0], got, .01); if(accum.polling_goal_achieved()) break; } } }