View | Details | Raw Unified | Return to bug 214338 | Differences between
and this patch

Collapse All | Expand All

(-)devel/glib20/Makefile (+2 lines)
Lines 96-101 Link Here
96
	@${REINPLACE_CMD} -e 's|inotify_support=yes|inotify_support=no| ; \
96
	@${REINPLACE_CMD} -e 's|inotify_support=yes|inotify_support=no| ; \
97
		s|-Werror|| ; \
97
		s|-Werror|| ; \
98
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
98
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
99
	@${CP} ${FILESDIR}/kqueue_fnm.c.in ${WRKSRC}/gio/kqueue/kqueue_fnm.c
100
	@${CP} ${FILESDIR}/kqueue_fnm.h.in ${WRKSRC}/gio/kqueue/kqueue_fnm.h
99
101
100
post-install:
102
post-install:
101
	@${MKDIR} ${STAGEDIR}${PREFIX}/share/GConf/gsettings
103
	@${MKDIR} ${STAGEDIR}${PREFIX}/share/GConf/gsettings
(-)devel/glib20/files/kqueue_fnm.c.in (+694 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 - 2017 Rozhuk Ivan <rozhuk.im@gmail.com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
27
 *
28
 */
29
30
#include <sys/param.h>
31
#include <sys/types.h>
32
#include <sys/event.h>
33
#include <sys/stat.h>
34
#include <sys/fcntl.h> /* open, fcntl */
35
36
#include <inttypes.h>
37
#include <stdlib.h> /* malloc, exit */
38
#include <unistd.h> /* close, write, sysconf */
39
#include <string.h> /* bcopy, bzero, memcpy, memmove, memset, strerror... */
40
#include <dirent.h> /* opendir, readdir */
41
#include <errno.h>
42
43
#include "kqueue_fnm.h"
44
45
46
/* Preallocate items count. */
47
#ifndef FILES_ALLOC_BLK_SIZE
48
#	define FILES_ALLOC_BLK_SIZE	32
49
#endif
50
51
typedef struct file_info_s { /* Directory file. */
52
	struct dirent 	de;		/* d_reclen used for action. */
53
	struct stat	sb;
54
} file_info_t, *file_info_p;
55
56
57
typedef struct readdir_data_s {
58
	int		fd;
59
	uint8_t		*buf;
60
	size_t		buf_size;
61
	size_t		buf_used;
62
	size_t		buf_pos;
63
} readdir_data_t, *readdir_data_p;
64
65
66
typedef struct kqueue_file_mon_data_s {
67
	int		fd;		/* fd for notify kqueue(). */
68
	int		is_dir;
69
	char		path[(PATH_MAX + 2)];
70
	size_t		path_size;
71
	size_t		name_offset;	/* Parent path size. */
72
	void		*udata;
73
	kqueue_fnm_p	kfnm;
74
	/* For dir. */
75
	file_info_p	files;
76
	volatile size_t	files_count;
77
	size_t		files_allocated;
78
} kqueue_file_mon_data_t;
79
80
81
typedef struct kqueue_file_nonify_monitor_s {
82
	int		fd;		/* kqueue() fd. */
83
	int		pfd[2];		/* pipe queue specific. */
84
	uint8_t		*tmpbuf;
85
	size_t		tmpbuf_size;
86
} kqueue_fnm_t;
87
88
89
typedef void (*kq_msg_cb)(kqueue_file_mon_data_p fmd);
90
91
typedef struct kqueue_file_mon_msg_pkt_s {
92
	size_t		magic;
93
	kq_msg_cb	msg_cb;
94
	kqueue_file_mon_data_p fmd;
95
	size_t		chk_sum;
96
} kqueue_fnm_msg_pkt_t, *kqueue_fnm_msg_pkt_p;
97
98
#define KF_MSG_PKT_MAGIC	0xffddaa00
99
100
101
#ifndef O_NOATIME
102
#	define O_NOATIME	0
103
#endif
104
#ifndef O_EVTONLY
105
#	define O_EVTONLY	O_RDONLY
106
#endif
107
#define OPEN_FILE_FLAGS		(O_EVTONLY | O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC)
108
109
#define EVFILT_VNODE_FLAGS_ALL	(NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE)
110
111
112
#ifndef _GENERIC_DIRSIZ
113
#	define _GENERIC_DIRSIZ(__de)	MIN((__de)->d_reclen, sizeof(struct dirent))
114
#endif
115
116
#define IS_NAME_DOTS(__name)	('.' == (__name)[0] &&			\
117
				 ('\0' == (__name)[1] || 		\
118
				  ('.' == (__name)[1] && '\0' == (__name)[2])))
119
#define IS_DE_NAME_EQ(__de1, __de2)  (0 == mem_cmpn((__de1)->d_name,	\
120
						    (__de1)->d_namlen,	\
121
						    (__de2)->d_name,	\
122
						    (__de2)->d_namlen))
123
#define zalloc(__size)		calloc(1, (__size))
124
125
126
static inline int
127
mem_cmpn(const void *buf1, const size_t buf1_size,
128
    const void *buf2, const size_t buf2_size) {
129
130
	if (buf1_size != buf2_size)
131
		return (((buf1_size > buf2_size) ? 127 : -127));
132
	if (0 == buf1_size || buf1 == buf2)
133
		return (0);
134
	if (NULL == buf1)
135
		return (-127);
136
	if (NULL == buf2)
137
		return (127);
138
	return (memcmp(buf1, buf2, buf1_size));
139
}
140
141
static inline int
142
realloc_items(void **items, const size_t item_size,
143
    size_t *allocated, const size_t alloc_blk_cnt, const size_t count) {
144
	size_t allocated_prev, allocated_new;
145
	uint8_t *items_new;
146
147
	if (NULL == items || 0 == item_size || NULL == allocated ||
148
	    0 == alloc_blk_cnt)
149
		return (EINVAL);
150
	allocated_prev = (*allocated);
151
	if (NULL != (*items) &&
152
	    allocated_prev > count &&
153
	    allocated_prev <= (count + alloc_blk_cnt))
154
		return (0);
155
	allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt);
156
#if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
157
	items_new = (uint8_t*)reallocarray((*items), item_size, allocated_new);
158
#else /* For old BSD systems. */
159
	items_new = (uint8_t*)realloc((*items), (item_size * allocated_new));
160
#endif
161
	if (NULL == items_new) /* Realloc fail! */
162
		return (ENOMEM);
163
164
	if (allocated_new > allocated_prev) { /* Init new mem. */
165
		bzero((items_new + (allocated_prev * item_size)),
166
		    ((allocated_new - allocated_prev) * item_size));
167
	}
168
	(*items) = items_new;
169
	(*allocated) = allocated_new;
170
171
	return (0);
172
}
173
174
175
static int
176
readdir_data_start(int fd, uint8_t *buf, size_t buf_size, readdir_data_p rdd) {
177
178
	if (-1 == fd || NULL == buf || 0 == buf_size || NULL == rdd)
179
		return (EINVAL);
180
	if (-1 == lseek(fd, 0, SEEK_SET))
181
		return (errno);
182
	bzero(rdd, sizeof(readdir_data_t));
183
	rdd->fd = fd;
184
	rdd->buf = buf;
185
	rdd->buf_size = buf_size;
186
187
	return (0);
188
}
189
190
static int
191
readdir_data_next(readdir_data_p rdd, struct dirent *de) {
192
	int ios;
193
	uint8_t *ptr;
194
195
	if (NULL == rdd || NULL == de)
196
		return (EINVAL);
197
198
retry:
199
	if (rdd->buf_used <= rdd->buf_pos) {
200
		ios = getdents(rdd->fd, (char*)rdd->buf, rdd->buf_size);
201
		if (-1 == ios)
202
			return (errno);
203
		rdd->buf_used = (size_t)ios;
204
		rdd->buf_pos = 0;
205
		if (0 == ios)
206
			return (ESPIPE); /* EOF. */
207
	}
208
	/* Keep data aligned. */
209
	ptr = (rdd->buf + rdd->buf_pos);
210
	memcpy(de, ptr, (sizeof(struct dirent) - sizeof(de->d_name)));
211
	if (0 == de->d_reclen)
212
		return (ESPIPE); /* EOF. */
213
	rdd->buf_pos += de->d_reclen;
214
#ifdef DT_WHT
215
	if (DT_WHT == de->d_type)
216
		goto retry;
217
#endif
218
	memcpy(de, ptr, _GENERIC_DIRSIZ(de));
219
	if (IS_NAME_DOTS(de->d_name))
220
		goto retry;
221
	return (0);
222
}
223
224
static void
225
kqueue_file_mon_data_clean(kqueue_file_mon_data_p fmd) {
226
227
	if (NULL == fmd)
228
		return;
229
	close(fmd->fd);
230
	fmd->fd = -1;
231
	fmd->files_count = 0;
232
	free(fmd->files);
233
	fmd->files = NULL;
234
}
235
236
static void
237
kqueue_file_mon_data_free(kqueue_file_mon_data_p fmd) {
238
239
	if (NULL == fmd)
240
		return;
241
	close(fmd->fd);
242
	free(fmd->files);
243
	free(fmd);
244
}
245
246
static kqueue_file_mon_data_p
247
kqueue_file_mon_data_alloc(kqueue_fnm_p kfnm, const char *path, void *udata) {
248
	kqueue_file_mon_data_p fmd;
249
250
	if (NULL == kfnm || NULL == path)
251
		return (NULL);
252
	fmd = zalloc(sizeof(kqueue_file_mon_data_t));
253
	if (NULL == fmd)
254
		return (NULL);
255
	/* Remember args. */
256
	fmd->path_size = strlcpy(fmd->path, path, PATH_MAX);
257
	fmd->name_offset = fmd->path_size;
258
	fmd->udata = udata;
259
	fmd->kfnm = kfnm;
260
261
	return (fmd);
262
}
263
264
static int
265
kqueue_file_mon_data_find_de_fileno(kqueue_file_mon_data_p fmd,
266
    struct dirent *de, size_t *idx) {
267
	size_t i;
268
269
	if (NULL == fmd || NULL == de || NULL == idx)
270
		return (0);
271
	for (i = 0; i < fmd->files_count; i ++) {
272
		if (de->d_fileno != fmd->files[i].de.d_fileno ||
273
		    de->d_type != fmd->files[i].de.d_type)
274
			continue;
275
		(*idx) = i;
276
		return (1);
277
	}
278
	(*idx) = fmd->files_count;
279
	return (0);
280
}
281
282
283
static void
284
kqueue_file_mon_data_init(kqueue_file_mon_data_p fmd) {
285
	struct dirent *de;
286
	struct stat sb;
287
	struct kevent kev;
288
	struct timespec ke_timeout;
289
	uint8_t *tmpbuf;
290
	readdir_data_t rdd;
291
292
	if (NULL == fmd)
293
		return;
294
	fmd->fd = open(fmd->path, OPEN_FILE_FLAGS);
295
	if (-1 == fmd->fd)
296
		return;
297
	if (0 != fstat(fmd->fd, &sb))
298
		goto err_out;
299
	/* Get parent folder name. */
300
	if (S_ISDIR(sb.st_mode)) {
301
		fmd->is_dir = 1;
302
		/* Be sure that folder contain trailing '/'. */
303
		if ('/' != fmd->path[(fmd->path_size - 1)]) {
304
			fmd->path[fmd->path_size] = '/';
305
			fmd->path_size ++;
306
			fmd->path[fmd->path_size] = 0;
307
		}
308
		/* Skip last '/' for parent dir search. */
309
		fmd->name_offset = (fmd->path_size - 1);
310
	}
311
	/* Find parent dir path size. */
312
	while (0 < fmd->name_offset && '/' != fmd->path[(fmd->name_offset - 1)]) {
313
		fmd->name_offset --;
314
	}
315
316
	/* Dir special processing. */
317
	if (0 != fmd->is_dir) {
318
		/* Read and remember dir content. */
319
		/* Get temp buf. */
320
		/* Do this once per dir handle. */
321
		if ((size_t)sb.st_blksize > fmd->kfnm->tmpbuf_size) {
322
			tmpbuf = realloc(fmd->kfnm->tmpbuf, sb.st_blksize);
323
			if (NULL == tmpbuf)
324
				goto err_out;
325
			fmd->kfnm->tmpbuf = tmpbuf;
326
			fmd->kfnm->tmpbuf_size = (size_t)sb.st_blksize;
327
		}
328
		if (0 != readdir_data_start(fmd->fd, fmd->kfnm->tmpbuf, sb.st_blksize, &rdd))
329
			goto err_out;
330
331
		for (;;) {
332
			if (0 != realloc_items((void**)&fmd->files,
333
			    sizeof(file_info_t), &fmd->files_allocated,
334
			    FILES_ALLOC_BLK_SIZE, fmd->files_count))
335
				goto err_out;
336
			de = &fmd->files[fmd->files_count].de; /* Use short name. */
337
			/* Get file name from folder. */
338
			if (0 != readdir_data_next(&rdd, de))
339
				break;
340
			/* Get file attrs. */
341
			if (0 != fstatat(fmd->fd, de->d_name,
342
			    &fmd->files[fmd->files_count].sb,
343
			    AT_SYMLINK_NOFOLLOW)) {
344
				bzero(&fmd->files[fmd->files_count].sb,
345
				    sizeof(struct stat));
346
			}
347
			fmd->files_count ++;
348
		}
349
	}
350
	/* Add to kqueue. */
351
	bzero(&kev, sizeof(kev));
352
	kev.ident = (uintptr_t)fmd->fd;
353
	kev.filter = EVFILT_VNODE;
354
	kev.flags = (EV_ADD | EV_CLEAR);
355
	kev.fflags = EVFILT_VNODE_FLAGS_ALL;
356
	kev.udata = (void*)fmd;
357
358
	bzero(&ke_timeout, sizeof(ke_timeout));
359
360
	if (-1 == kevent(fmd->kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
361
		goto err_out;
362
	return; /* OK. */
363
364
err_out:
365
	kqueue_file_mon_data_clean(fmd);
366
}
367
368
369
static void
370
kqueue_handle_changes(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd,
371
    kfnm_event_handler_cb cb_func) {
372
	file_info_t fi;
373
	size_t i, j, k;
374
	readdir_data_t rdd;
375
376
	if (NULL == kfnm || NULL == fmd || NULL == cb_func)
377
		return;
378
	if (0 != fstat(fmd->fd, &fi.sb) ||
379
	    0 == fi.sb.st_nlink) {
380
		kqueue_file_mon_data_clean(fmd);
381
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
382
		    fmd->path, "", NULL);
383
		return;
384
	}
385
	if (0 == fmd->is_dir) {
386
		fmd->path[fmd->name_offset] = 0;
387
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED, fmd->path,
388
		    (fmd->path + fmd->name_offset), NULL);
389
		fmd->path[fmd->name_offset] = '/';
390
		return;
391
	}
392
	/* Dir processing. */
393
	if (0 != readdir_data_start(fmd->fd, fmd->kfnm->tmpbuf, fi.sb.st_blksize, &rdd))
394
		return;
395
	/* Mark all as removed. */
396
	for (i = 0; i < fmd->files_count; i ++) {
397
		fmd->files[i].de.d_reclen = KF_EVENT_DELETED;
398
	}
399
	/* Update dir. */
400
	while (0 == readdir_data_next(&rdd, &fi.de)) {
401
		/* Get file stat. */
402
		if (0 != fstatat(fmd->fd, fi.de.d_name, &fi.sb, AT_SYMLINK_NOFOLLOW)) {
403
			bzero(&fi.sb, sizeof(struct stat)); /* Fail, set to zero. */
404
		}
405
		/* Is new file/folder? */
406
		if (0 == kqueue_file_mon_data_find_de_fileno(fmd, &fi.de, &i)) { /* Add new. */
407
			if (0 != realloc_items((void**)&fmd->files,
408
			    sizeof(file_info_t), &fmd->files_allocated,
409
			    FILES_ALLOC_BLK_SIZE, fmd->files_count))
410
				goto err_out;
411
			memcpy(&fmd->files[fmd->files_count], &fi, sizeof(file_info_t));
412
			fmd->files[fmd->files_count].de.d_reclen = KF_EVENT_CREATED;
413
			fmd->files_count ++;
414
			/* Notify. */
415
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CREATED,
416
			    fmd->path, fi.de.d_name, NULL);
417
			continue;
418
		}
419
		/* Is renamed? */
420
		if (0 == IS_DE_NAME_EQ(&fi.de, &fmd->files[i].de)) {
421
			/* Notify. */
422
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
423
			    fmd->path, fmd->files[i].de.d_name, fi.de.d_name);
424
			/* Update. */
425
			memcpy(&fmd->files[i], &fi, sizeof(file_info_t));
426
			fmd->files[i].de.d_reclen = KF_EVENT_RENAMED;
427
			continue;
428
		}
429
		/* Is modified? */
430
		if (0 != memcmp(&fmd->files[i].sb, &fi.sb, sizeof(struct stat))) {
431
			memcpy(&fmd->files[i].sb, &fi.sb, sizeof(struct stat)); /* Update stat. */
432
			fmd->files[i].de.d_reclen = KF_EVENT_CHANGED;
433
			/* Notify. */
434
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
435
			    fmd->path, fi.de.d_name, NULL);
436
			continue;
437
		}
438
		/* Not changed. */
439
		fmd->files[i].de.d_reclen = KF_EVENT_NOT_CHANGED;
440
	}
441
	/* Remove marked as removed. */
442
	for (i = 0; i < fmd->files_count; i ++) {
443
		if (KF_EVENT_DELETED != fmd->files[i].de.d_reclen)
444
			continue;
445
		/* Look for next non removed item + notify. */
446
		for (j = (i + 1); j < fmd->files_count; j ++) {
447
			if (KF_EVENT_DELETED != fmd->files[j].de.d_reclen)
448
				break;
449
		}
450
		/* Notify. */
451
		for (k = i; k < j; k ++) {
452
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
453
			    fmd->path, fmd->files[k].de.d_name, NULL);
454
		}
455
		memmove(&fmd->files[i], &fmd->files[j],
456
		    (sizeof(file_info_t) * (fmd->files_count - j)));
457
		fmd->files_count -= (j - i);
458
	}
459
460
err_out:
461
	return;
462
}
463
464
static void
465
kqueue_handle_rename(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd,
466
    kfnm_event_handler_cb cb_func) {
467
	int up_dir_fd, found = 0;
468
	readdir_data_t rdd;
469
	struct dirent de;
470
	struct stat sb;
471
	char old_filename[(MAXNAMLEN + 2)];
472
	size_t old_filename_size;
473
474
	if (NULL == kfnm || NULL == fmd || NULL == cb_func)
475
		return;
476
	if (0 != fstat(fmd->fd, &sb) ||
477
	    0 == sb.st_nlink) {
478
notify_removed:
479
		kqueue_file_mon_data_clean(fmd);
480
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
481
		    fmd->path, "", NULL);
482
		return;
483
	}
484
	/* Save old file name. */
485
	old_filename_size = (fmd->path_size - fmd->name_offset - fmd->is_dir);
486
	memcpy(old_filename,
487
	    (fmd->path + fmd->name_offset),
488
	    old_filename_size);
489
	old_filename[old_filename_size] = 0;
490
491
	/* Get parent folder name. */
492
	fmd->path[fmd->name_offset] = 0;
493
	/* Try to open. */
494
	up_dir_fd = open(fmd->path, (OPEN_FILE_FLAGS | O_DIRECTORY));
495
	/* Restore '/' after parent folder. */
496
	fmd->path[fmd->name_offset] = '/';
497
	if (-1 == up_dir_fd ||
498
	    0 != fstat(up_dir_fd, &sb) ||
499
	    0 != readdir_data_start(up_dir_fd, fmd->kfnm->tmpbuf, sb.st_blksize, &rdd)) {
500
		close(up_dir_fd);
501
		return;
502
	}
503
	/* Find new name by inode. */
504
	while (0 == readdir_data_next(&rdd, &de)) {
505
		if (de.d_fileno == sb.st_ino) {
506
			found ++;
507
			break;
508
		}
509
	}
510
	close(up_dir_fd);
511
	if (0 == found)
512
		goto notify_removed; /* Not found. */
513
	/* Update name. */
514
	if (PATH_MAX <= (fmd->name_offset + de.d_namlen))
515
		return; /* Too long. */
516
	memcpy((fmd->path + fmd->name_offset), de.d_name, de.d_namlen);
517
	fmd->path_size = (fmd->name_offset + de.d_namlen);
518
	/* Add last '/' for dir. */
519
	fmd->path[fmd->path_size] = '/';
520
	fmd->path_size += fmd->is_dir;
521
	fmd->path[fmd->path_size] = 0;
522
	/* Notify. */
523
	cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
524
	    fmd->path, old_filename, de.d_name);
525
}
526
527
528
static void
529
kqueue_fnm_delay_call_process(kqueue_fnm_p kfnm, kq_msg_cb forced_msg_cb) {
530
	ssize_t ios;
531
	kqueue_fnm_msg_pkt_t msg;
532
533
	for (;;) {
534
		ios = read(kfnm->pfd[0], &msg, sizeof(msg));
535
		if (0 >= ios)
536
			return;
537
		if (sizeof(msg) != ios ||
538
		    KF_MSG_PKT_MAGIC != msg.magic ||
539
		    (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd)) != msg.chk_sum)
540
			continue;
541
		if (NULL != forced_msg_cb) {
542
			forced_msg_cb(msg.fmd);
543
			continue;
544
		}
545
		if (NULL == msg.msg_cb)
546
			continue;
547
		msg.msg_cb(msg.fmd);
548
	}
549
}
550
551
static int
552
kqueue_fnm_delay_call(kqueue_fnm_p kfnm, kq_msg_cb msg_cb,
553
    kqueue_file_mon_data_p fmd) {
554
	kqueue_fnm_msg_pkt_t msg;
555
556
	if (NULL == kfnm || NULL == fmd)
557
		return (EINVAL);
558
	msg.magic = KF_MSG_PKT_MAGIC;
559
	msg.msg_cb = msg_cb;
560
	msg.fmd = fmd;
561
	msg.chk_sum = (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd));
562
	if (sizeof(msg) == write(kfnm->pfd[1], &msg, sizeof(msg)))
563
		return (0);
564
	return (errno);
565
}
566
567
568
void
569
kqueue_fnm_free(kqueue_fnm_p kfnm) {
570
571
	if (NULL == kfnm)
572
		return;
573
	close(kfnm->fd);
574
	/* Free all in delay calls queue. */
575
	kqueue_fnm_delay_call_process(kfnm, kqueue_file_mon_data_free);
576
	close(kfnm->pfd[0]);
577
	close(kfnm->pfd[1]);
578
	free(kfnm->tmpbuf);
579
	free(kfnm);
580
}
581
582
kqueue_fnm_p
583
kqueue_fnm_create(void) {
584
	kqueue_fnm_p kfnm;
585
	struct kevent kev;
586
	struct timespec ke_timeout;
587
588
	kfnm = zalloc(sizeof(kqueue_fnm_t));
589
	if (NULL == kfnm)
590
		return (NULL);
591
	kfnm->fd = kqueue();
592
	if (-1 == kfnm->fd)
593
		goto err_out;
594
	if (-1 == pipe2(kfnm->pfd, O_NONBLOCK))
595
		goto err_out;
596
597
	bzero(&kev, sizeof(kev));
598
	kev.ident = kfnm->pfd[0];
599
	kev.filter = EVFILT_READ;
600
	kev.flags = (EV_ADD | EV_ENABLE);
601
602
	bzero(&ke_timeout, sizeof(ke_timeout));
603
604
	if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
605
		goto err_out;
606
	return (kfnm);
607
608
err_out:
609
	kqueue_fnm_free(kfnm);
610
	return (NULL);
611
}
612
613
kqueue_file_mon_data_p
614
kqueue_fnm_add(kqueue_fnm_p kfnm, const char *path, void *udata) {
615
	int error;
616
	kqueue_file_mon_data_p fmd;
617
	
618
	if (NULL == kfnm || NULL == path)
619
		return (NULL);
620
	fmd = kqueue_file_mon_data_alloc(kfnm, path, udata);
621
	if (NULL == fmd)
622
		return (NULL);
623
	/* Shedule delay call to init. */
624
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_init, fmd);
625
	if (0 != error) { /* Error, do no directly init to avoid freezes. */
626
		kqueue_file_mon_data_free(fmd);
627
		return (NULL);
628
	}
629
	return (fmd);
630
}
631
632
void
633
kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
634
	int error;
635
636
	if (NULL == kfnm || NULL == fmd)
637
		return;
638
	/* Cancel notifications. */
639
	close(fmd->fd);
640
	fmd->fd = -1;
641
	/* Shedule delay call to free. */
642
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_free, fmd);
643
	if (0 == error)
644
		return;
645
	/* Error, free directly. */
646
	kqueue_file_mon_data_free(fmd);
647
}
648
649
650
int
651
kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm) {
652
653
	if (NULL == kfnm)
654
		return (-1);
655
	return (kfnm->fd);
656
}
657
658
void
659
kqueue_fnm_proccess_events(kqueue_fnm_p kfnm, kfnm_event_handler_cb cb_func) {
660
	struct kevent kev;
661
	struct timespec ke_timeout;
662
	kqueue_file_mon_data_p fmd;
663
664
	if (NULL == kfnm || NULL == cb_func)
665
		return;
666
	/* Get and proccess events. */
667
	bzero(&ke_timeout, sizeof(ke_timeout));
668
	while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, &ke_timeout)) {
669
		if (kev.ident == (uintptr_t)kfnm->pfd[0] &&
670
		    kev.filter == EVFILT_READ) { /* Handle delay calls. */
671
			kqueue_fnm_delay_call_process(kfnm, NULL);
672
			continue;
673
		}
674
		if (EVFILT_VNODE != kev.filter ||
675
		    0 == kev.udata)
676
			continue; /* Unknown event or no associated data, skip. */
677
		fmd = (kqueue_file_mon_data_p)kev.udata;
678
679
		if (EV_ERROR & kev.flags) {
680
			kev.flags |= NOTE_REVOKE; /* Treat error as unmount. */
681
		}
682
		if (NOTE_RENAME & kev.fflags) {
683
			kqueue_handle_rename(kfnm, fmd, cb_func);
684
		}
685
		if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB) & kev.fflags) {
686
			kqueue_handle_changes(kfnm, fmd, cb_func);
687
		}
688
		if ((NOTE_DELETE | NOTE_REVOKE) & kev.fflags) {
689
			kqueue_file_mon_data_clean(fmd);
690
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
691
			    fmd->path, "", NULL);
692
		}
693
	}
694
}
(-)devel/glib20/files/kqueue_fnm.h.in (+68 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 - 2017 Rozhuk Ivan <rozhuk.im@gmail.com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
27
 *
28
 */
29
30
31
32
#ifndef __KQUEUE_FILE_NOTIFY_MONITOR_H__
33
#define __KQUEUE_FILE_NOTIFY_MONITOR_H__
34
35
#include <sys/param.h>
36
#include <sys/types.h>
37
#include <inttypes.h>
38
39
40
typedef struct kqueue_file_nonify_monitor_s	*kqueue_fnm_p;
41
typedef struct kqueue_file_mon_data_s		*kqueue_file_mon_data_p;
42
43
typedef void (*kfnm_event_handler_cb)(kqueue_fnm_p kfnm,
44
			       kqueue_file_mon_data_p fmd, void *udata,
45
			       uint32_t event,
46
			       const char *base,
47
			       const char *filename,
48
			       const char *new_filename);
49
#define KF_EVENT_NOT_CHANGED	0 /* Internal use. */
50
#define KF_EVENT_CREATED	1
51
#define KF_EVENT_DELETED	2
52
#define KF_EVENT_RENAMED	3
53
#define KF_EVENT_CHANGED	4
54
55
56
kqueue_fnm_p	kqueue_fnm_create(void);
57
void		kqueue_fnm_free(kqueue_fnm_p kfnm);
58
59
kqueue_file_mon_data_p	kqueue_fnm_add(kqueue_fnm_p kfnm,
60
			    const char *path, void *udata);
61
void		kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd);
62
63
int		kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm);
64
void		kqueue_fnm_proccess_events(kqueue_fnm_p kfnm,
65
		    kfnm_event_handler_cb cb_func);
66
67
68
#endif /* __KQUEUE_FILE_NOTIFY_MONITOR_H__ */
(-)devel/glib20/files/patch-bug739424 (-59 lines)
Lines 1-59 Link Here
1
From 22656f16c29591207c667362e2a42fd348fe8494 Mon Sep 17 00:00:00 2001
2
From: Martin Pieuchot <mpi@openbsd.org>
3
Date: Fri, 28 Apr 2017 15:06:52 +0200
4
Subject: [PATCH] kqueue: fix use-after-free of ``kqueue_sub''.
5
6
Since ``kqueue_sub'' are not refcounted it is common to see a thread
7
freeing one of them while another thread is manipulating them.  This
8
leads to crashs reported in:
9
	https://bugzilla.gnome.org/show_bug.cgi?id=739424
10
11
To prevent such crash, make sure the threads are holding ``hash_lock''
12
when manipulating such items.
13
---
14
 gio/kqueue/kqueue-helper.c | 6 ++++--
15
 1 file changed, 4 insertions(+), 2 deletions(-)
16
17
diff --git a/gio/kqueue/kqueue-helper.c b/gio/kqueue/kqueue-helper.c
18
index d4e66cd4d..84b9ef164 100644
19
--- gio/kqueue/kqueue-helper.c
20
+++ gio/kqueue/kqueue-helper.c
21
@@ -291,10 +291,10 @@ process_kqueue_notifications (GIOChannel   *gioc,
22
 
23
   G_LOCK (hash_lock);
24
   sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd));
25
-  G_UNLOCK (hash_lock);
26
 
27
   if (sub == NULL)
28
     {
29
+      G_UNLOCK (hash_lock);
30
       KH_W ("Got a notification for a deleted or non-existing subscription %d",
31
              n.fd);
32
       return TRUE;
33
@@ -336,6 +336,7 @@ process_kqueue_notifications (GIOChannel   *gioc,
34
         g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, g_get_monotonic_time ());
35
     }
36
 
37
+  G_UNLOCK (hash_lock);
38
   return TRUE;
39
 }
40
 
41
@@ -451,13 +452,14 @@ _kh_start_watching (kqueue_sub *sub)
42
 
43
   G_LOCK (hash_lock);
44
   g_hash_table_insert (subs_hash_table, GINT_TO_POINTER (sub->fd), sub);
45
-  G_UNLOCK (hash_lock);
46
 
47
   _kqueue_thread_push_fd (sub->fd);
48
   
49
   /* Bump the kqueue thread. It will pick up a new sub entry to monitor */
50
   if (!_ku_write (kqueue_socket_pair[0], "A", 1))
51
     KH_W ("Failed to bump the kqueue thread (add fd, error %d)", errno);
52
+  G_UNLOCK (hash_lock);
53
+
54
   return TRUE;
55
 }
56
 
57
-- 
58
2.12.2
59
(-)devel/glib20/files/patch-bug778515 (-55 lines)
Lines 1-55 Link Here
1
From e305fe971e4647d971428a772b7290b9c308a96f Mon Sep 17 00:00:00 2001
2
From: Steven McDonald <steven@steven-mcdonald.id.au>
3
Date: Sun, 12 Feb 2017 11:02:55 +1100
4
Subject: gio: Always purge kqueue subs from missing list
5
6
Previously, _kh_cancel_sub assumed that it only needed to call
7
_km_remove if sub did not exist in subs_hash_table. This is erroneous
8
because the complementary operation, _km_add_missing, can be called
9
from process_kqueue_notifications, in which context sub can *only* have
10
come from subs_hash_table.
11
12
Since _km_remove is implemented using g_slist_remove, which is
13
documented to be a noop if the list does not contain the element to be
14
removed, it is safe to call _km_remove unconditionally here.
15
16
https://bugzilla.gnome.org/show_bug.cgi?id=778515
17
---
18
 gio/kqueue/kqueue-helper.c | 15 +++++----------
19
 1 file changed, 5 insertions(+), 10 deletions(-)
20
21
diff --git a/gio/kqueue/kqueue-helper.c b/gio/kqueue/kqueue-helper.c
22
index 4671396..d4e66cd 100644
23
--- gio/kqueue/kqueue-helper.c
24
+++ gio/kqueue/kqueue-helper.c
25
@@ -498,22 +498,17 @@ _kh_add_sub (kqueue_sub *sub)
26
 gboolean
27
 _kh_cancel_sub (kqueue_sub *sub)
28
 {
29
-  gboolean missing = FALSE;
30
+  gboolean removed = FALSE;
31
   g_assert (kqueue_socket_pair[0] != -1);
32
   g_assert (sub != NULL);
33
 
34
+  _km_remove (sub);
35
+
36
   G_LOCK (hash_lock);
37
-  missing = !g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd));
38
+  removed = g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd));
39
   G_UNLOCK (hash_lock);
40
 
41
-  if (missing)
42
-    {
43
-      /* If there were no fd for this subscription, file is still
44
-       * missing. */
45
-      KH_W ("Removing subscription from missing");
46
-      _km_remove (sub);
47
-    }
48
-  else
49
+  if (removed)
50
     {
51
       /* fd will be closed in the kqueue thread */
52
       _kqueue_thread_remove_fd (sub->fd);
53
-- 
54
cgit v0.12
55
(-)devel/glib20/files/patch-gio_kqueue_Makefile.am (+26 lines)
Line 0 Link Here
1
--- gio/kqueue/Makefile.am.orig	2015-10-14 14:41:16.000000000 +0300
2
+++ gio/kqueue/Makefile.am	2016-11-06 05:08:37.646089000 +0300
3
@@ -4,21 +4,8 @@
4
 
5
 libkqueue_la_SOURCES = \
6
        gkqueuefilemonitor.c \
7
-       gkqueuefilemonitor.h \
8
-       kqueue-helper.c \
9
-       kqueue-helper.h \
10
-       kqueue-thread.c \
11
-       kqueue-thread.h \
12
-       kqueue-sub.c \
13
-       kqueue-sub.h \
14
-       kqueue-missing.c \
15
-       kqueue-missing.h \
16
-       kqueue-utils.c \
17
-       kqueue-utils.h \
18
-       kqueue-exclusions.c \
19
-       kqueue-exclusions.h \
20
-       dep-list.c \
21
-       dep-list.h \
22
+       kqueue_fnm.c \
23
+       kqueue_fnm.h \
24
        $(NULL)
25
 
26
 libkqueue_la_CFLAGS = \
(-)devel/glib20/files/patch-gio_kqueue_gkqueuefilemonitor.c (+372 lines)
Line 0 Link Here
1
--- gio/kqueue/gkqueuefilemonitor.c.orig	2015-10-14 14:41:16.000000000 +0300
2
+++ gio/kqueue/gkqueuefilemonitor.c	2016-11-06 05:01:49.712277000 +0300
3
@@ -1,198 +1,203 @@
4
-/*******************************************************************************
5
-  Copyright (c) 2011, 2012 Dmitry Matveev <me@dmitrymatveev.co.uk>
6
-
7
-  Permission is hereby granted, free of charge, to any person obtaining a copy
8
-  of this software and associated documentation files (the "Software"), to deal
9
-  in the Software without restriction, including without limitation the rights
10
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
-  copies of the Software, and to permit persons to whom the Software is
12
-  furnished to do so, subject to the following conditions:
13
-
14
-  The above copyright notice and this permission notice shall be included in
15
-  all copies or substantial portions of the Software.
16
-
17
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
-  THE SOFTWARE.
24
-*******************************************************************************/
25
+/*-
26
+ * Copyright (c) 2016 Rozhuk Ivan <rozhuk.im@gmail.com>
27
+ * All rights reserved.
28
+ *
29
+ * Redistribution and use in source and binary forms, with or without
30
+ * modification, are permitted provided that the following conditions
31
+ * are met:
32
+ * 1. Redistributions of source code must retain the above copyright
33
+ *    notice, this list of conditions and the following disclaimer.
34
+ * 2. Redistributions in binary form must reproduce the above copyright
35
+ *    notice, this list of conditions and the following disclaimer in the
36
+ *    documentation and/or other materials provided with the distribution.
37
+ *
38
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48
+ * SUCH DAMAGE.
49
+ *
50
+ * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
51
+ *
52
+ */
53
 
54
 #include "config.h"
55
 
56
-#include "gkqueuefilemonitor.h"
57
-#include "kqueue-helper.h"
58
-#include "kqueue-exclusions.h"
59
-#include <gio/gpollfilemonitor.h>
60
-#include <gio/gfile.h>
61
+#include <glib-object.h>
62
+#include <string.h>
63
+#include <gio/gfilemonitor.h>
64
+#include <gio/glocalfilemonitor.h>
65
 #include <gio/giomodule.h>
66
+#include "glib-private.h"
67
+#include <glib-unix.h>
68
+#include "kqueue_fnm.h"
69
 
70
 
71
-struct _GKqueueFileMonitor
72
-{
73
-  GLocalFileMonitor parent_instance;
74
+static GMutex			kqueue_lock;
75
+static GSource			*kqueue_source = NULL;
76
+static volatile kqueue_fnm_p	kqueue_fnm = NULL;
77
 
78
-  kqueue_sub *sub;
79
+#define G_TYPE_KQUEUE_FILE_MONITOR      (g_kqueue_file_monitor_get_type())
80
+#define G_KQUEUE_FILE_MONITOR(inst)     (G_TYPE_CHECK_INSTANCE_CAST((inst), \
81
+					 G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor))
82
 
83
-  GFileMonitor *fallback;
84
-  GFile *fbfile;
85
-};
86
+typedef GLocalFileMonitorClass	GKqueueFileMonitorClass;
87
 
88
-static gboolean g_kqueue_file_monitor_cancel (GFileMonitor* monitor);
89
+typedef struct {
90
+	GLocalFileMonitor	parent_instance;
91
+	kqueue_file_mon_data_p	fmd;
92
+} GKqueueFileMonitor;
93
 
94
+GType g_kqueue_file_monitor_get_type(void);
95
 G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
96
-       g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
97
+       g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
98
                g_define_type_id,
99
                "kqueue",
100
-               20))
101
+               10))
102
 
103
 
104
 static void
105
-_fallback_callback (GFileMonitor      *unused,
106
-                    GFile             *first,
107
-                    GFile             *second,
108
-                    GFileMonitorEvent  event,
109
-                    gpointer           udata)
110
-{
111
-  GKqueueFileMonitor *kq_mon = G_KQUEUE_FILE_MONITOR (udata);
112
-  GFileMonitor *mon = G_FILE_MONITOR (kq_mon);
113
-  g_assert (kq_mon != NULL);
114
-  g_assert (mon != NULL);
115
-  (void) unused;
116
-
117
-  if (event == G_FILE_MONITOR_EVENT_CHANGED)
118
-    {
119
-      GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (kq_mon);
120
-
121
-      _kh_dir_diff (kq_mon->sub, local_monitor->source);
122
-    }
123
-  else
124
-    g_file_monitor_emit_event (mon, first, second, event);
125
-}
126
-
127
-
128
-static void
129
-g_kqueue_file_monitor_finalize (GObject *object)
130
-{
131
-  GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (object);
132
-
133
-  if (kqueue_monitor->sub)
134
-    {
135
-      _kh_cancel_sub (kqueue_monitor->sub);
136
-      _kh_sub_free (kqueue_monitor->sub);
137
-      kqueue_monitor->sub = NULL;
138
-    }
139
-
140
-  if (kqueue_monitor->fallback)
141
-    g_object_unref (kqueue_monitor->fallback);
142
-
143
-  if (kqueue_monitor->fbfile)
144
-    g_object_unref (kqueue_monitor->fbfile);
145
-
146
-  if (G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize)
147
-    (*G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) (object);
148
-}
149
-
150
-static void
151
-g_kqueue_file_monitor_start (GLocalFileMonitor *local_monitor,
152
-                             const gchar *dirname,
153
-                             const gchar *basename,
154
-                             const gchar *filename,
155
-                             GFileMonitorSource *source)
156
-{
157
-  GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (local_monitor);
158
-  GObject *obj;
159
-  GKqueueFileMonitorClass *klass;
160
-  GObjectClass *parent_class;
161
-  kqueue_sub *sub = NULL;
162
-  gboolean ret_kh_startup = FALSE;
163
-  const gchar *path = NULL; 
164
-
165
-
166
-  ret_kh_startup = _kh_startup ();
167
-  g_assert (ret_kh_startup);
168
-
169
-  path = filename;
170
-  if (!path)
171
-    path = dirname;
172
-
173
-  /* For a directory monitor, create a subscription object anyway.
174
-   * It will be used for directory diff calculation routines. 
175
-   * Wait, directory diff in a GKqueueFileMonitor?
176
-   * Yes, it is. When a file monitor is started on an non-existent
177
-   * file, GIO uses a GKqueueFileMonitor object for that. If a directory
178
-   * will be created under that path, GKqueueFileMonitor will have to
179
-   * handle the directory notifications. */
180
-
181
-  sub = _kh_sub_new (path, TRUE, source);
182
-
183
-  /* FIXME: what to do about errors here? we can't return NULL or another
184
-   * kind of error and an assertion is probably too hard (same issue as in
185
-   * the inotify backend) */
186
-  g_assert (sub != NULL);
187
-  kqueue_monitor->sub = sub;
188
-
189
-  if (!_ke_is_excluded (path))
190
-    _kh_add_sub (sub);
191
-  else
192
-    {
193
-      GFile *file = g_file_new_for_path (path);
194
-      kqueue_monitor->fbfile = file;
195
-      kqueue_monitor->fallback = _g_poll_file_monitor_new (file);
196
-      g_signal_connect (kqueue_monitor->fallback,
197
-                        "changed",
198
-                        G_CALLBACK (_fallback_callback),
199
-                        kqueue_monitor);
200
-    }
201
+kqueue_event_handler(kqueue_fnm_p kfnm,
202
+    kqueue_file_mon_data_p fmd, void *udata, uint32_t event,
203
+    const char *base, const char *filename, const char *new_filename) {
204
+	static const uint32_t kfnm_to_glib_map[] = {
205
+		0,				/* KF_EVENT_NOT_CHANGED */
206
+		G_FILE_MONITOR_EVENT_CREATED,	/* KF_EVENT_CREATED */
207
+		G_FILE_MONITOR_EVENT_DELETED,	/* KF_EVENT_DELETED */
208
+		G_FILE_MONITOR_EVENT_RENAMED,	/* KF_EVENT_RENAMED */
209
+		G_FILE_MONITOR_EVENT_CHANGED	/* KF_EVENT_CHANGED */
210
+	};
211
+
212
+	if (NULL == kfnm || NULL == filename ||
213
+	    KF_EVENT_CREATED > event ||
214
+	    KF_EVENT_CHANGED < event)
215
+		return;
216
+	g_file_monitor_source_handle_event(udata,
217
+	    kfnm_to_glib_map[event],
218
+	    filename, new_filename, NULL,
219
+	    g_source_get_time(kqueue_source));
220
+}
221
+
222
+static gboolean
223
+g_kqueue_file_monitor_callback(gint fd, GIOCondition condition, gpointer user_data) {
224
+
225
+	/* Only one thread process events. */
226
+	g_mutex_lock(&kqueue_lock);
227
+	kqueue_fnm_proccess_events(kqueue_fnm, kqueue_event_handler);
228
+	g_mutex_unlock(&kqueue_lock);
229
+
230
+	return (TRUE);
231
+}
232
+
233
+static gboolean
234
+g_kqueue_file_monitor_is_supported(void) {
235
+
236
+	if (NULL != kqueue_fnm)
237
+		return (TRUE);
238
+	/* Init only once. */
239
+	g_mutex_lock(&kqueue_lock);
240
+	if (NULL != kqueue_fnm) {
241
+		g_mutex_unlock(&kqueue_lock);
242
+		return (TRUE); /* Initialized while wait lock. */
243
+	}
244
+	kqueue_fnm = kqueue_fnm_create();
245
+	if (NULL == kqueue_fnm) {
246
+		g_mutex_unlock(&kqueue_lock);
247
+		return (FALSE); /* Init fail. */
248
+	}
249
+	kqueue_source = g_unix_fd_source_new(kqueue_fnm_get_ev_recv_fd(kqueue_fnm), G_IO_IN);
250
+	g_source_set_callback(kqueue_source, (GSourceFunc)g_kqueue_file_monitor_callback, NULL, NULL);
251
+	g_source_attach(kqueue_source, GLIB_PRIVATE_CALL(g_get_worker_context)());
252
+	g_mutex_unlock(&kqueue_lock);
253
+
254
+	return (TRUE);
255
 }
256
 
257
 static gboolean
258
-g_kqueue_file_monitor_is_supported (void)
259
-{
260
-  return _kh_startup ();
261
+g_kqueue_file_monitor_cancel(GFileMonitor *monitor) {
262
+	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor);
263
+
264
+	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
265
+	gffm->fmd = NULL;
266
+
267
+	return (TRUE);
268
 }
269
 
270
 static void
271
-g_kqueue_file_monitor_class_init (GKqueueFileMonitorClass *klass)
272
-{
273
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
274
-  GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
275
-  GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass);
276
+g_kqueue_file_monitor_finalize(GObject *object) {
277
+	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object);
278
 
279
-  gobject_class->finalize = g_kqueue_file_monitor_finalize;
280
-  file_monitor_class->cancel = g_kqueue_file_monitor_cancel;
281
+	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
282
+	gffm->fmd = NULL;
283
+}
284
 
285
-  local_file_monitor_class->is_supported = g_kqueue_file_monitor_is_supported;
286
-  local_file_monitor_class->start = g_kqueue_file_monitor_start;
287
-  local_file_monitor_class->mount_notify = TRUE; /* TODO: ??? */
288
+static void
289
+g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor,
290
+    const gchar *dirname, const gchar *basename,
291
+    const gchar *filename, GFileMonitorSource *source) {
292
+	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR (local_monitor);
293
+
294
+	g_assert(NULL != kqueue_fnm);
295
+	g_source_ref((GSource*)source);
296
+
297
+	if (NULL == filename) {
298
+		filename = dirname;
299
+	}
300
+	gffm->fmd = kqueue_fnm_add(kqueue_fnm, filename, source);
301
 }
302
 
303
 static void
304
-g_kqueue_file_monitor_init (GKqueueFileMonitor *monitor)
305
-{
306
+g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) {
307
+	
308
 }
309
 
310
-static gboolean
311
-g_kqueue_file_monitor_cancel (GFileMonitor *monitor)
312
-{
313
-  GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (monitor);
314
-
315
-  if (kqueue_monitor->sub)
316
-    {
317
-      _kh_cancel_sub (kqueue_monitor->sub);
318
-      _kh_sub_free (kqueue_monitor->sub);
319
-      kqueue_monitor->sub = NULL;
320
-    }
321
-  else if (kqueue_monitor->fallback)
322
-    {
323
-      g_signal_handlers_disconnect_by_func (kqueue_monitor->fallback, _fallback_callback, kqueue_monitor);
324
-      g_file_monitor_cancel (kqueue_monitor->fallback);
325
-    }
326
+static void
327
+g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) {
328
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
329
+	GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class);
330
+
331
+	class->is_supported = g_kqueue_file_monitor_is_supported;
332
+	class->start = g_kqueue_file_monitor_start;
333
+	class->mount_notify = TRUE; /* TODO: ??? */
334
+	file_monitor_class->cancel = g_kqueue_file_monitor_cancel;
335
+	gobject_class->finalize = g_kqueue_file_monitor_finalize;
336
+}
337
+
338
+static void
339
+g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) {
340
+	
341
+}
342
+
343
+void
344
+g_io_module_load(GIOModule *module) {
345
+
346
+	g_type_module_use(G_TYPE_MODULE(module));
347
+
348
+	g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
349
+	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
350
+	g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
351
+	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
352
+}
353
+
354
+void
355
+g_io_module_unload(GIOModule *module) {
356
+	
357
+	g_assert_not_reached();
358
+}
359
 
360
-  if (G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel)
361
-    (*G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) (monitor);
362
+char **
363
+g_io_module_query(void) {
364
+	char *eps[] = {
365
+		G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
366
+		G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
367
+		NULL
368
+	};
369
 
370
-  return TRUE;
371
+	return (g_strdupv(eps));
372
 }

Return to bug 214338