Line data Source code
1 : /**
2 : * This file is part of rmlint.
3 : *
4 : * rmlint is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * rmlint is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with rmlint. If not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Authors:
18 : *
19 : * - Christopher <sahib> Pahl 2010-2015 (https://github.com/sahib)
20 : * - Daniel <SeeSpotRun> T. 2014-2015 (https://github.com/SeeSpotRun)
21 : *
22 : * Hosted on http://github.com/sahib/rmlint
23 : *
24 : */
25 :
26 : #ifndef RM_UTILITIES_H_INCLUDE
27 : #define RM_UTILITIES_H_INCLUDE
28 :
29 : #include <glib.h>
30 : #include <stdbool.h>
31 :
32 : #include <time.h>
33 : #include <unistd.h>
34 : #include <sys/stat.h>
35 : #include <fcntl.h>
36 : #include <sys/uio.h>
37 :
38 : /* Pat(h)tricia Trie implementation */
39 : #include "pathtricia.h"
40 :
41 : #if HAVE_STAT64 && !RM_IS_APPLE
42 : typedef struct stat64 RmStat;
43 : #else
44 : typedef struct stat RmStat;
45 : #endif
46 :
47 : ////////////////////////////////////
48 : // SYSCALL WRAPPERS //
49 : ////////////////////////////////////
50 :
51 3016155 : static inline int rm_sys_stat(const char *path, RmStat *buf) {
52 : #if HAVE_STAT64 && !RM_IS_APPLE
53 3016155 : return stat64(path, buf);
54 : #else
55 : return stat(path, buf);
56 : #endif
57 : }
58 :
59 101323 : static inline int rm_sys_lstat(const char *path, RmStat *buf) {
60 : #if HAVE_STAT64 && !RM_IS_APPLE
61 101323 : return lstat64(path, buf);
62 : #else
63 : return lstat(path, buf);
64 : #endif
65 : }
66 :
67 338118 : static inline int rm_sys_stat_mtime_seconds(RmStat *stat) {
68 : #if RM_IS_APPLE
69 : return stat->st_mtimespec.tv_sec;
70 : #else
71 338118 : return stat->st_mtim.tv_sec;
72 : #endif
73 : }
74 :
75 221718 : static inline int rm_sys_open(const char *path, int mode) {
76 : #if HAVE_STAT64
77 : #ifdef O_LARGEFILE
78 221718 : mode |= O_LARGEFILE;
79 : #endif
80 : #endif
81 :
82 221718 : return open(path, mode, (S_IRUSR | S_IWUSR));
83 : }
84 :
85 221724 : static inline void rm_sys_close(int fd) {
86 221724 : if(close(fd) == -1) {
87 0 : rm_log_perror("close(2) failed");
88 : }
89 221724 : }
90 :
91 226111 : static inline gint64 rm_sys_preadv(int fd, const struct iovec *iov, int iovcnt,
92 : RmOff offset) {
93 : #if RM_IS_APPLE
94 : if(lseek(fd, offset, SEEK_SET) == -1) {
95 : rm_log_perror("seek in emulated preadv failed");
96 : }
97 : return readv(fd, iov, iovcnt);
98 : #elif RM_PLATFORM_32
99 : if(lseek64(fd, offset, SEEK_SET) == -1) {
100 : rm_log_perror("seek in emulated preadv failed");
101 : }
102 : return readv(fd, iov, iovcnt);
103 : #else
104 226111 : return preadv(fd, iov, iovcnt, offset);
105 : #endif
106 : }
107 :
108 : /////////////////////////////////////
109 : // UID/GID VALIDITY CHECKING //
110 : /////////////////////////////////////
111 :
112 : typedef struct RmUserList {
113 : GSequence *users;
114 : GSequence *groups;
115 : GMutex mutex;
116 : } RmUserList;
117 :
118 : /**
119 : * @brief Create a new list of users.
120 : */
121 : RmUserList *rm_userlist_new(void);
122 :
123 : /**
124 : * @brief Check if a uid and gid is contained in the list.
125 : *
126 : * @param valid_uid (out)
127 : * @param valid_gid (out)
128 : *
129 : * @return true if both are valid.
130 : */
131 : bool rm_userlist_contains(RmUserList *list, unsigned long uid, unsigned gid,
132 : bool *valid_uid, bool *valid_gid);
133 :
134 : /**
135 : * @brief Deallocate the memory allocated by rm_userlist_new()
136 : */
137 : void rm_userlist_destroy(RmUserList *list);
138 :
139 : /**
140 : * @brief Get the name of the user running rmlint.
141 : */
142 : char *rm_util_get_username(void);
143 :
144 : /**
145 : * @brief Get the group of the user running rmlint.
146 : */
147 : char *rm_util_get_groupname(void);
148 :
149 : ////////////////////////////////////
150 : // GENERAL UTILITES //
151 : ////////////////////////////////////
152 :
153 : #define RM_LIST_NEXT(node) ((node) ? node->next : NULL)
154 :
155 : /**
156 : * @brief Replace {subs} with {with} in {string}
157 : *
158 : * @return a newly allocated string, g_free it.
159 : */
160 : char *rm_util_strsub(const char *string, const char *subs, const char *with);
161 :
162 : /**
163 : * @brief Check if a file has a invalid gid/uid or both.
164 : *
165 : * @return the appropiate RmLintType for the file
166 : */
167 : int rm_util_uid_gid_check(RmStat *statp, RmUserList *userlist);
168 :
169 : /**
170 : * @brief Check if a file is a binary that is not stripped.
171 : *
172 : * @path: Path to the file to be checked.
173 : * @statp: valid stat pointer with st_mode filled (allow-none).
174 : *
175 : * @return: if it is a binary with debug symbols.
176 : */
177 : bool rm_util_is_nonstripped(const char *path, RmStat *statp);
178 :
179 : /**
180 : * @brief Get the basename part of the file. It does not change filename.
181 : *
182 : * @return NULL on failure, the pointer after the last / on success.
183 : */
184 : char *rm_util_basename(const char *filename);
185 :
186 : /**
187 : * @brief Check if the file or any components of it are hidden.
188 : *
189 : * @return true if it is.
190 : */
191 : bool rm_util_path_is_hidden(const char *path);
192 :
193 : /**
194 : * @brief Get the depth of a path
195 : *
196 : * @param path
197 : *
198 : * @return depth of path or 0.
199 : */
200 : int rm_util_path_depth(const char *path);
201 :
202 : typedef gpointer (*RmNewFunc)(void);
203 :
204 : /**
205 : * @brief A setdefault supplementary function for GHashTable.
206 : *
207 : * This is about the same as dict.setdefault in python.
208 : *
209 : * @param table the table to use
210 : * @param key key to lookup
211 : * @param default_func if the key does not exist in table, return default_func
212 : * and insert it into table
213 : *
214 : * @return value, which may be default_func() if key does not exist.
215 : */
216 : GQueue *rm_hash_table_setdefault(GHashTable *table, gpointer key, RmNewFunc default_func);
217 :
218 : /**
219 : * @brief Return a pointer to the extension part of the file or NULL if none.
220 : *
221 : * @return: a pointer >= basename or NULL.
222 : */
223 : char *rm_util_path_extension(const char *basename);
224 :
225 : /**
226 : * @brief Get the inode of the directory of the file specified in path.
227 : */
228 : ino_t rm_util_parent_node(const char *path);
229 :
230 : /*
231 : * @brief Takes num and converts into some human readable string. 1024 -> 1KB
232 : */
233 : void rm_util_size_to_human_readable(RmOff num, char *in, gsize len);
234 :
235 : /////////////////////////////////////
236 : // MOUNTTABLE IMPLEMENTATION //
237 : /////////////////////////////////////
238 :
239 : typedef struct RmMountTable {
240 : GHashTable *part_table;
241 : GHashTable *disk_table;
242 : GHashTable *nfs_table;
243 : GHashTable *evilfs_table;
244 : GHashTable *reflinkfs_table;
245 : } RmMountTable;
246 :
247 : /**
248 : * @brief Allocates a new mounttable.
249 : * @param force_fiemap Create random fiemap data always. Useful for testing.
250 : *
251 : * @return The mounttable. Free with rm_mounts_table_destroy.
252 : */
253 : RmMountTable *rm_mounts_table_new(bool force_fiemap);
254 :
255 : /**
256 : * @brief Destroy a previously allocated mounttable.
257 : *
258 : * @param self the table to destroy.
259 : */
260 : void rm_mounts_table_destroy(RmMountTable *self);
261 :
262 : /**
263 : * @brief Check if the device is or is part of a nonrotational device.
264 : *
265 : * This operation has constant time.
266 : *
267 : * @param self the table to lookup from.
268 : * @param device the dev_t of a file, e.g. looked up from rm_sys_stat(2)
269 : *
270 : * @return true if it is non a nonrational device.
271 : */
272 : bool rm_mounts_is_nonrotational(RmMountTable *self, dev_t device);
273 :
274 : /**
275 : * @brief Return name of device/disk.
276 : *
277 : * This operation has constant time.
278 : *
279 : * @param self the table to lookup from.
280 : * @param device the dev_t of a disk
281 : *
282 : * @return pointer to disk name.
283 : */
284 : char *rm_mounts_get_disk_name(RmMountTable *self, dev_t device);
285 :
286 : /**
287 : * @brief Get the disk behind the partition.
288 : *
289 : * @param self the table to lookup from.
290 : * @param partition the dev_t of a partition (sda1 -> 8:1), e.g. looked up from
291 : *rm_sys_stat(2)
292 : *
293 : * @return the dev_t of the whole disk. (sda 8:0)
294 : */
295 : dev_t rm_mounts_get_disk_id(RmMountTable *self, dev_t partition, const char *path);
296 :
297 : /**
298 : * @brief Same as above, but calls rm_sys_stat(2) on path for you.
299 : */
300 : dev_t rm_mounts_get_disk_id_by_path(RmMountTable *self, const char *path);
301 :
302 : /**
303 : * @brief Indicates true if dev_t points to a filesystem that might confuse
304 : * rmlint.
305 : */
306 : bool rm_mounts_is_evil(RmMountTable *self, dev_t to_check);
307 :
308 : /**
309 : * @brief Indicates true if source and dest are on same partition, and the
310 : * partition supports reflink copies (cp --reflink).
311 : */
312 : bool rm_mounts_can_reflink(RmMountTable *self, dev_t source, dev_t dest);
313 :
314 : /////////////////////////////////
315 : // FIEMAP IMPLEMENATION //
316 : /////////////////////////////////
317 :
318 : /**
319 : * @brief Lookup the physical offset of a file fd at any given offset.
320 : *
321 : * @return the physical offset starting from the disk.
322 : */
323 : RmOff rm_offset_get_from_fd(int fd, RmOff file_offset, RmOff *file_offset_next);
324 :
325 : /**
326 : * @brief Lookup the physical offset of a file path at any given offset.
327 : *
328 : * @return the physical offset starting from the disk.
329 : */
330 : RmOff rm_offset_get_from_path(const char *path, RmOff file_offset,
331 : RmOff *file_offset_next);
332 :
333 : /**
334 : * @brief Test if two files have identical fiemaps.
335 : */
336 : bool rm_offsets_match(char *path1, char *path2);
337 :
338 : //////////////////////////////
339 : // TIMESTAMP HELPERS //
340 : //////////////////////////////
341 :
342 : /**
343 : * @brief Parse a ISO8601 timestamp to a unix timestamp.
344 : */
345 : time_t rm_iso8601_parse(const char *string);
346 :
347 : /**
348 : * @brief convert a unix timestamp as iso8601 timestamp string.
349 : *
350 : * @param stamp unix timestamp
351 : * @param buf result buffer to hold the string.
352 : * @param buf_size sizeof buf.
353 : *
354 : * @return true if conversion succeeded.
355 : */
356 : bool rm_iso8601_format(time_t stamp, char *buf, gsize buf_size);
357 :
358 : ///////////////////////////////
359 : // THREADPOOL HELPERS //
360 : ///////////////////////////////
361 :
362 : /**
363 : * @brief Create a new GThreadPool with default cfg.
364 : *
365 : * @param func func to execute
366 : * @param data user_data to pass
367 : * @param threads how many threads at max to use.
368 : *
369 : * @return newly allocated GThreadPool
370 : */
371 : GThreadPool *rm_util_thread_pool_new(GFunc func, gpointer data, int threads);
372 :
373 : /**
374 : * @brief Push a new job to a threadpool.
375 : *
376 : * @return true on success.
377 : */
378 : bool rm_util_thread_pool_push(GThreadPool *pool, gpointer data);
379 :
380 : /////////////////////////////////////
381 : // JSON CACHE IMPLEMENTATION //
382 : /////////////////////////////////////
383 :
384 : /**
385 : * @brief Read json_path and write (path:cksum) into cksum_table.
386 : *
387 : * @param file_trie to set read cksums in.
388 : * @param json_path path with .json file.
389 : *
390 : * @return 0 on success.
391 : */
392 : int rm_json_cache_read(RmTrie *file_trie, const char *json_path);
393 :
394 : #endif /* RM_UTILITIES_H_INCLUDE*/
|