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