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             version(ARM){
66                 bool s_isdir = S_ISDIR(cast(ushort)stat_buf.st_mode);
67                 bool s_isreg = S_ISREG(cast(ushort)stat_buf.st_mode);
68             }
69             else {
70                 bool s_isdir = S_ISDIR(stat_buf.st_mode);
71                 bool s_isreg = S_ISREG(stat_buf.st_mode);
72             }
73             if (s_isdir)
74             {
75                 addDirectory(full_path);
76             }
77             else if (s_isreg && (stat_buf.st_mode & S_IROTH))
78             {
79                 int fd = .open(full_path.toStringz, O_RDONLY | O_NOCTTY);
80                 
81                 if (fd > 0)
82                     return fd;
83             }
84         }
85         
86         return -1;
87     }
88 
89 private:
90     void addDirectory(in string dirname)
91     {
92         m_dirlist.insertBack(dirname);
93     }
94     
95     Pair!(dirent*, string) getNextDirent()
96     {
97         while (m_cur_dir.first)
98         {
99             if (dirent* dir = readdir(m_cur_dir.first))
100                 return makePair(dir, m_cur_dir.second);
101             
102             closedir(m_cur_dir.first);
103             m_cur_dir = makePair!(DIR*, string)(null, "");
104             
105             while (!m_dirlist.empty && !m_cur_dir.first)
106             {
107                 const string next_dir_name = m_dirlist.front;
108                 m_dirlist = Vector!string(m_dirlist[1 .. $]);
109                 
110                 if (DIR* next_dir = opendir(next_dir_name.toStringz))
111                     m_cur_dir = makePair(next_dir, next_dir_name);
112             }
113         }
114         
115         return Pair!(dirent*, string)(); // nothing left
116     }
117     
118     Pair!(DIR*, string) m_cur_dir;
119     Vector!string m_dirlist;
120 }
121 
122 
123 interface FileDescriptorSource
124 {
125 public:
126     abstract int nextFd();
127 
128 }
129 
130 /**
131 * File Tree Walking Entropy Source
132 */
133 final class ProcWalkingEntropySource : EntropySource
134 {
135 public:
136     @property string name() const { return "Proc Walker"; }
137 
138     void poll(ref EntropyAccumulator accum)
139     {
140         __gshared immutable size_t MAX_FILES_READ_PER_POLL = 2048;
141         const double ENTROPY_ESTIMATE = 1.0 / (8*1024);
142         
143         if (!m_dir)
144             m_dir = new DirectoryWalker(m_path);
145         
146         SecureVector!ubyte* io_buffer = &accum.getIoBuffer(4096);
147         foreach (size_t i; 0 .. MAX_FILES_READ_PER_POLL)
148         {
149             int fd = m_dir.nextFd();
150             
151             // If we've exhaused this walk of the directory, halt the poll
152             if (fd == -1)
153             {
154                 destroy(m_dir);
155                 m_dir = null;
156                 break;
157             }
158             
159             ssize_t got = .read(fd, io_buffer.ptr, 4096);
160             close(fd);
161             
162             if (got > 0)
163                 accum.add(io_buffer.ptr, got, ENTROPY_ESTIMATE);
164             
165             if (accum.pollingGoalAchieved())
166                 break;
167         }
168     }
169 
170     this(in string root_dir)
171     {
172         m_path = root_dir;
173     }
174 
175     ~this() { if (m_dir) destroy(m_dir); }
176 
177 private:
178     const string m_path;
179     FileDescriptorSource m_dir;
180 }
181 
182 @nogc nothrow pure private:
183 bool S_ISTYPE( mode_t mode, uint mask ) { return ( mode & S_IFMT ) == mask; }
184 bool S_ISDIR( mode_t mode )  { return S_ISTYPE( mode, S_IFDIR );  }
185 bool S_ISREG( mode_t mode )  { return S_ISTYPE( mode, S_IFREG );  }