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