1 /* diritor.h: Iterator through entries in a directory.
2  *
3  * Copyright (C) 2007,2008,2010,2011,2014 Olly Betts
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  */
19 
20 #ifndef OMEGA_INCLUDED_DIRITOR_H
21 #define OMEGA_INCLUDED_DIRITOR_H
22 
23 #include <string>
24 
25 #include "safedirent.h"
26 #include "safeerrno.h"
27 #include "safefcntl.h"
28 #include "safesysstat.h"
29 
30 #include <sys/types.h>
31 
32 #ifndef __WIN32__
33 #include <grp.h> // For getgrgid().
34 #include <pwd.h> // For getpwuid().
35 #endif
36 
37 #ifdef HAVE_MAGIC_H
38 #include <magic.h>
39 #endif
40 
41 #include "common/noreturn.h"
42 
43 #include "loadfile.h"
44 #include "runfilter.h" // For class ReadError.
45 
46 class DirectoryIterator {
47 #if defined O_NOATIME && O_NOATIME != 0
48     static uid_t euid;
49 #endif
50 
51 #ifdef HAVE_MAGIC_H
52     static magic_t magic_cookie;
53 #endif
54 
55     std::string path;
56     std::string::size_type path_len;
57 
58     DIR * dir;
59     struct dirent *entry;
60     struct stat statbuf;
61     bool statbuf_valid;
62     bool follow_symlinks;
63 
64     void call_stat();
65 
ensure_statbuf_valid()66     void ensure_statbuf_valid() {
67 	if (!statbuf_valid) {
68 	    call_stat();
69 	    statbuf_valid = true;
70 	}
71     }
72 
73     void build_path();
74 
75   public:
76 
DirectoryIterator(bool follow_symlinks_)77     explicit DirectoryIterator(bool follow_symlinks_)
78 	: dir(NULL), follow_symlinks(follow_symlinks_) { }
79 
~DirectoryIterator()80     ~DirectoryIterator() {
81 	if (dir) closedir(dir);
82     }
83 
84     /// Start iterating through entries in @a path.
85     //
86     //  Throws a std::string exception upon failure.
87     void start(const std::string & path);
88 
89     /// Read the next directory entry which doesn't start with ".".
90     //
91     //  We do this to skip ".", "..", and Unix hidden files.
92     //
93     //  @return false if there are no more entries.
next()94     bool next() {
95 	path.resize(path_len);
96 	errno = 0;
97 	do {
98 	    entry = readdir(dir);
99 	} while (entry && entry->d_name[0] == '.');
100 	statbuf_valid = false;
101 	if (entry == NULL && errno != 0) next_failed();
102 	return (entry != NULL);
103     }
104 
105     XAPIAN_NORETURN(void next_failed() const);
106 
leafname()107     const char * leafname() const { return entry->d_name; }
108 
pathname()109     const std::string & pathname() const { return path; }
110 
111     typedef enum { REGULAR_FILE, DIRECTORY, OTHER } type;
112 
get_type()113     type get_type() {
114 #ifdef DT_UNKNOWN
115 	/* Possible values:
116 	 * DT_UNKNOWN DT_FIFO DT_CHR DT_DIR DT_BLK DT_REG DT_LNK DT_SOCK DT_WHT
117 	 */
118 	switch (entry->d_type) {
119 	    case DT_UNKNOWN:
120 		// The current filing system doesn't support d_type.
121 		break;
122 	    case DT_REG:
123 		return REGULAR_FILE;
124 	    case DT_DIR:
125 		return DIRECTORY;
126 #ifdef HAVE_LSTAT
127 	    case DT_LNK:
128 		if (follow_symlinks) break;
129 		return OTHER;
130 #endif
131 	    default:
132 		return OTHER;
133 	}
134 #endif
135 
136 	ensure_statbuf_valid();
137 
138 	if (S_ISREG(statbuf.st_mode)) return REGULAR_FILE;
139 	if (S_ISDIR(statbuf.st_mode)) return DIRECTORY;
140 	return OTHER;
141     }
142 
get_size()143     off_t get_size() {
144 	ensure_statbuf_valid();
145 	return statbuf.st_size;
146     }
147 
get_mtime()148     time_t get_mtime() {
149 	ensure_statbuf_valid();
150 	return statbuf.st_mtime;
151     }
152 
get_owner()153     const char * get_owner() {
154 #ifndef __WIN32__
155 	ensure_statbuf_valid();
156 	struct passwd * pwentry = getpwuid(statbuf.st_uid);
157 	return pwentry ? pwentry->pw_name : NULL;
158 #else
159 	return NULL;
160 #endif
161     }
162 
get_group()163     const char * get_group() {
164 #ifndef __WIN32__
165 	ensure_statbuf_valid();
166 	struct group * grentry = getgrgid(statbuf.st_gid);
167 	return grentry ? grentry->gr_name : NULL;
168 #else
169 	return NULL;
170 #endif
171     }
172 
is_owner_readable()173     bool is_owner_readable() {
174 	ensure_statbuf_valid();
175 #ifndef __WIN32__
176 	return (statbuf.st_mode & S_IRUSR);
177 #else
178 	return (statbuf.st_mode & S_IREAD);
179 #endif
180     }
181 
is_group_readable()182     bool is_group_readable() {
183 	ensure_statbuf_valid();
184 #ifndef __WIN32__
185 	return (statbuf.st_mode & S_IRGRP);
186 #else
187 	return false;
188 #endif
189     }
190 
is_other_readable()191     bool is_other_readable() {
192 	ensure_statbuf_valid();
193 #ifndef __WIN32__
194 	return (statbuf.st_mode & S_IROTH);
195 #else
196 	return false;
197 #endif
198     }
199 
try_noatime()200     bool try_noatime() {
201 #if defined O_NOATIME && O_NOATIME != 0
202 	if (euid == 0) return true;
203 	ensure_statbuf_valid();
204 	return statbuf.st_uid == euid;
205 #else
206 	return false;
207 #endif
208     }
209 
210 #ifdef HAVE_MAGIC_H
211     std::string get_magic_mimetype();
212 #else
get_magic_mimetype()213     std::string get_magic_mimetype() { return std::string(); }
214 #endif
215 
file_to_string()216     std::string file_to_string() {
217 	build_path();
218 	std::string out;
219 	int flags = NOCACHE;
220 	if (try_noatime()) flags |= NOATIME;
221 	if (!load_file(path, out, flags)) throw ReadError();
222 	return out;
223     }
224 };
225 
226 #endif // OMEGA_INCLUDED_DIRITOR_H
227