1 /*
2     clsync - file tree sync utility based on inotify/kqueue
3 
4     Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "common.h"
21 #include "port-hacks.h"
22 #include "error.h"
23 #include "sync.h"
24 #include "indexes.h"
25 #include "mon_inotify.h"
26 
27 enum event_bits {
28 	UEM_DIR		= 0x01,
29 	UEM_CREATED	= 0x02,
30 	UEM_DELETED	= 0x04,
31 };
32 
33 struct recognize_event_return {
34 	union {
35 		struct {
36 			eventobjtype_t objtype_old:16;
37 			eventobjtype_t objtype_new:16;
38 		} v;
39 		uint32_t i;
40 	} u;
41 };
42 
recognize_event(uint32_t event)43 static inline uint32_t recognize_event(uint32_t event) {
44 	struct recognize_event_return r = {{{0}}};
45 
46 	eventobjtype_t type;
47 	int is_created;
48 	int is_deleted;
49 
50 	type = (event & IN_ISDIR ? EOT_DIR : EOT_FILE);
51 	is_created = event & (IN_CREATE|IN_MOVED_TO);
52 	is_deleted = event & (IN_DELETE_SELF|IN_DELETE|IN_MOVED_FROM);
53 
54 	debug(4, "type == %x; is_created == %x; is_deleted == %x", type, is_created, is_deleted);
55 
56 	r.u.v.objtype_old = type;
57 	r.u.v.objtype_new = type;
58 
59 	if (is_created)
60 		r.u.v.objtype_old = EOT_DOESNTEXIST;
61 
62 	if (is_deleted)
63 		r.u.v.objtype_new = EOT_DOESNTEXIST;
64 
65 	return r.u.i;
66 }
67 
inotify_add_watch_dir(ctx_t * ctx_p,indexes_t * indexes_p,const char * const accpath)68 int inotify_add_watch_dir(ctx_t *ctx_p, indexes_t *indexes_p, const char *const accpath) {
69 	int inotify_d = (int)(long)ctx_p->fsmondata;
70 	return inotify_add_watch(inotify_d, accpath, INOTIFY_MARKMASK);
71 }
72 
inotify_wait(ctx_t * ctx_p,struct indexes * indexes_p,struct timeval * tv_p)73 int inotify_wait(ctx_t *ctx_p, struct indexes *indexes_p, struct timeval *tv_p) {
74 	int inotify_d = (int)(long)ctx_p->fsmondata;
75 
76 	debug(3, "select with timeout %li secs.", tv_p->tv_sec);
77 	fd_set rfds;
78 	FD_ZERO(&rfds);
79 	FD_SET(inotify_d, &rfds);
80 	return select(inotify_d+1, &rfds, NULL, NULL, tv_p);
81 }
82 
83 #define INOTIFY_HANDLE_CONTINUE {\
84 	ptr += sizeof(struct inotify_event) + event->len;\
85 	count++;\
86 	continue;\
87 }
88 
inotify_handle(ctx_t * ctx_p,indexes_t * indexes_p)89 int inotify_handle(ctx_t *ctx_p, indexes_t *indexes_p) {
90 	static struct timeval tv={0};
91 	int inotify_d = (int)(long)ctx_p->fsmondata;
92 
93 	int count = 0;
94 
95 	fd_set rfds;
96 	FD_ZERO(&rfds);
97 	FD_SET(inotify_d, &rfds);
98 
99 	char   *path_rel	= NULL;
100 	size_t  path_rel_len	= 0;
101 	char   *path_full	= NULL;
102 	size_t  path_full_size	= 0;
103 	while (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
104 
105 		char buf[BUFSIZ + 1];
106 		size_t r = read(inotify_d, buf, BUFSIZ);
107 		if (r <= 0) {
108 			error("Got error while reading events from inotify with read().");
109 			count = -1;
110 			goto l_inotify_handle_end;
111 		}
112 
113 #ifdef PARANOID
114 		g_hash_table_remove_all(indexes_p->fpath2ei_ht);
115 #endif
116 
117 		char *ptr =  buf;
118 		char *end = &buf[r];
119 		while (ptr < end) {
120 			struct inotify_event *event = (struct inotify_event *)ptr;
121 
122 			// Removing stale wd-s
123 
124 			if(event->mask & IN_IGNORED) {
125 				debug(2, "Cleaning up info about watch descriptor %i.", event->wd);
126 				indexes_remove_bywd(indexes_p, event->wd);
127 				INOTIFY_HANDLE_CONTINUE;
128 			}
129 
130 			// Getting path
131 
132 			char *fpath = indexes_wd2fpath(indexes_p, event->wd);
133 
134 			if(fpath == NULL) {
135 				debug(2, "Event %p on stale watch (wd: %i).", (void *)(long)event->mask, event->wd);
136 				INOTIFY_HANDLE_CONTINUE;
137 			}
138 			debug(2, "Event %p on \"%s\" (wd: %i; fpath: \"%s\").", (void *)(long)event->mask, event->len>0?event->name:"", event->wd, fpath);
139 
140 			// Getting full path
141 
142 			size_t path_full_memreq = strlen(fpath) + event->len + 2;
143 			if (path_full_size < path_full_memreq) {
144 				path_full      = xrealloc(path_full, path_full_memreq);
145 				path_full_size = path_full_memreq;
146 			}
147 
148 			if (event->len>0)
149 				sprintf(path_full, "%s/%s", fpath, event->name);
150 			else
151 				sprintf(path_full, "%s", fpath);
152 
153 			// Getting infomation about file/dir/etc
154 
155 			stat64_t lstat;
156 			mode_t st_mode;
157 			size_t st_size;
158 			if (lstat64(path_full, &lstat)) {
159 				debug(2, "Cannot lstat64(\"%s\", lstat). Seems, that the object disappeared.", path_full);
160 				if(event->mask & IN_ISDIR)
161 					st_mode = S_IFDIR;
162 				else
163 					st_mode = S_IFREG;
164 				st_size = 0;
165 			} else {
166 				st_mode = lstat.st_mode;
167 				st_size = lstat.st_size;
168 			}
169 
170 			struct  recognize_event_return r;
171 			r.u.i = recognize_event(event->mask);
172 
173 			if (sync_prequeue_loadmark(1, ctx_p, indexes_p, path_full, NULL, r.u.v.objtype_old, r.u.v.objtype_new, event->mask, event->wd, st_mode, st_size, &path_rel, &path_rel_len, NULL)) {
174 				count = -1;
175 				goto l_inotify_handle_end;
176 			}
177 
178 			INOTIFY_HANDLE_CONTINUE;
179 		}
180 
181 		// Globally queueing captured events:
182 		// Moving events from local queue to global ones
183 		sync_prequeue_unload(ctx_p, indexes_p);
184 	}
185 
186 l_inotify_handle_end:
187 	if(path_full != NULL)
188 		free(path_full);
189 
190 	if(path_rel != NULL)
191 		free(path_rel);
192 
193 	return count;
194 }
195 
inotify_deinit(ctx_t * ctx_p)196 int inotify_deinit(ctx_t *ctx_p) {
197 	int inotify_d = (int)(long)ctx_p->fsmondata;
198 	debug(3, "Closing inotify_d");
199 	return close(inotify_d);
200 }
201 
202