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

Collapse All | Expand All

(-)devel/glib20/Makefile (-1 / +13 lines)
Lines 48-54 Link Here
48
		glib-compile-resources.1 gresource.1 gdbus-codegen.1
48
		glib-compile-resources.1 gresource.1 gdbus-codegen.1
49
gobject_MAN=	glib-genmarshal.1 glib-mkenums.1 gobject-query.1
49
gobject_MAN=	glib-genmarshal.1 glib-mkenums.1 gobject-query.1
50
50
51
OPTIONS_DEFINE=	COLLATION_FIX DEBUG NLS
51
OPTIONS_DEFINE=	COLLATION_FIX DEBUG FAM_ALTBACKEND NLS
52
OPTIONS_SUB=	yes
52
OPTIONS_SUB=	yes
53
# libc collation was fixed by https://svnweb.freebsd.org/changeset/base/290494
53
# libc collation was fixed by https://svnweb.freebsd.org/changeset/base/290494
54
COLLATION_FIX_DESC=	Use ICU for UTF-8 string collation (if libc is broken)
54
COLLATION_FIX_DESC=	Use ICU for UTF-8 string collation (if libc is broken)
Lines 65-70 Link Here
65
# http://www.freebsd.org/cgi/query-pr.cgi?pr=175930 see comment by ed@
65
# http://www.freebsd.org/cgi/query-pr.cgi?pr=175930 see comment by ed@
66
#COLLATION_FIX_CONFIGURE_ENV+=	CFLAGS="-D__STDC_ISO_10646__"
66
#COLLATION_FIX_CONFIGURE_ENV+=	CFLAGS="-D__STDC_ISO_10646__"
67
67
68
FAM_ALTBACKEND_DESC=		Alternate file monitor backend
69
FAM_ALTBACKEND_USES+=		autoreconf:build
70
FAM_ALTBACKEND_EXTRA_PATCHES=	${FILESDIR}/extra-patch-gio_kqueue_Makefile.am
71
68
.include <bsd.port.pre.mk>
72
.include <bsd.port.pre.mk>
69
73
70
# doesn't build yet
74
# doesn't build yet
Lines 90-99 Link Here
90
.endif
94
.endif
91
CONFIGURE_TARGET=${GLIB_ARCH}-portbld-freebsd${OSREL}
95
CONFIGURE_TARGET=${GLIB_ARCH}-portbld-freebsd${OSREL}
92
96
97
post-patch-FAM_ALTBACKEND-on:
98
	@${CP} -f ${FILESDIR}/gkqueuefilemonitor.c.in ${WRKSRC}/gio/kqueue/gkqueuefilemonitor.c
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
101
	@(cd ${WRKSRC} && ${AUTORECONF} -fvi)
102
93
post-patch:
103
post-patch:
94
	@${REINPLACE_CMD} -e 's|/usr/local|${LOCALBASE}|g ; \
104
	@${REINPLACE_CMD} -e 's|/usr/local|${LOCALBASE}|g ; \
95
		s|/usr/share/locale/locale|${LOCALBASE}/share/locale/locale|g' \
105
		s|/usr/share/locale/locale|${LOCALBASE}/share/locale/locale|g' \
96
			${WRKSRC}/glib/gutils.c
106
			${WRKSRC}/glib/gutils.c
107
108
pre-configure:
97
	@${REINPLACE_CMD} -e 's|inotify_support=yes|inotify_support=no| ; \
109
	@${REINPLACE_CMD} -e 's|inotify_support=yes|inotify_support=no| ; \
98
		s|-Werror|| ; \
110
		s|-Werror|| ; \
99
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
111
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
(-)devel/glib20/files/extra-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/gkqueuefilemonitor.c.in (+203 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 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 "config.h"
31
32
#include <glib-object.h>
33
#include <string.h>
34
#include <gio/gfilemonitor.h>
35
#include <gio/glocalfilemonitor.h>
36
#include <gio/giomodule.h>
37
#include "glib-private.h"
38
#include <glib-unix.h>
39
#include "kqueue_fnm.h"
40
41
42
static GMutex			kqueue_lock;
43
static GSource			*kqueue_source = NULL;
44
static volatile kqueue_fnm_p	kqueue_fnm = NULL;
45
46
#define G_TYPE_KQUEUE_FILE_MONITOR      (g_kqueue_file_monitor_get_type())
47
#define G_KQUEUE_FILE_MONITOR(inst)     (G_TYPE_CHECK_INSTANCE_CAST((inst), \
48
					 G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor))
49
50
typedef GLocalFileMonitorClass	GKqueueFileMonitorClass;
51
52
typedef struct {
53
	GLocalFileMonitor	parent_instance;
54
	kqueue_file_mon_data_p	fmd;
55
} GKqueueFileMonitor;
56
57
GType g_kqueue_file_monitor_get_type(void);
58
G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
59
       g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
60
               g_define_type_id,
61
               "kqueue",
62
               10))
63
64
65
static void
66
kqueue_event_handler(kqueue_fnm_p kfnm,
67
    kqueue_file_mon_data_p fmd, void *udata, uint32_t event,
68
    const char *base, const char *filename, const char *new_filename) {
69
	static const uint32_t kfnm_to_glib_map[] = {
70
		0,				/* KF_EVENT_NOT_CHANGED */
71
		G_FILE_MONITOR_EVENT_CREATED,	/* KF_EVENT_CREATED */
72
		G_FILE_MONITOR_EVENT_DELETED,	/* KF_EVENT_DELETED */
73
		G_FILE_MONITOR_EVENT_RENAMED,	/* KF_EVENT_RENAMED */
74
		G_FILE_MONITOR_EVENT_CHANGED	/* KF_EVENT_CHANGED */
75
	};
76
77
	if (NULL == kfnm || NULL == filename ||
78
	    KF_EVENT_CREATED > event ||
79
	    KF_EVENT_CHANGED < event)
80
		return;
81
	g_file_monitor_source_handle_event(udata,
82
	    kfnm_to_glib_map[event],
83
	    filename, new_filename, NULL,
84
	    g_source_get_time(kqueue_source));
85
}
86
87
static gboolean
88
g_kqueue_file_monitor_callback(gint fd, GIOCondition condition, gpointer user_data) {
89
90
	/* Only one thread process events. */
91
	g_mutex_lock(&kqueue_lock);
92
	kqueue_fnm_proccess_events(kqueue_fnm, kqueue_event_handler);
93
	g_mutex_unlock(&kqueue_lock);
94
95
	return (TRUE);
96
}
97
98
static gboolean
99
g_kqueue_file_monitor_is_supported(void) {
100
101
	if (NULL != kqueue_fnm)
102
		return (TRUE);
103
	/* Init only once. */
104
	g_mutex_lock(&kqueue_lock);
105
	if (NULL != kqueue_fnm) {
106
		g_mutex_unlock(&kqueue_lock);
107
		return (TRUE); /* Initialized while wait lock. */
108
	}
109
	kqueue_fnm = kqueue_fnm_create();
110
	if (NULL == kqueue_fnm) {
111
		g_mutex_unlock(&kqueue_lock);
112
		return (FALSE); /* Init fail. */
113
	}
114
	kqueue_source = g_unix_fd_source_new(kqueue_fnm_get_ev_recv_fd(kqueue_fnm), G_IO_IN);
115
	g_source_set_callback(kqueue_source, (GSourceFunc)g_kqueue_file_monitor_callback, NULL, NULL);
116
	g_source_attach(kqueue_source, GLIB_PRIVATE_CALL(g_get_worker_context)());
117
	g_mutex_unlock(&kqueue_lock);
118
119
	return (TRUE);
120
}
121
122
static gboolean
123
g_kqueue_file_monitor_cancel(GFileMonitor *monitor) {
124
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor);
125
126
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
127
	gffm->fmd = NULL;
128
129
	return (TRUE);
130
}
131
132
static void
133
g_kqueue_file_monitor_finalize(GObject *object) {
134
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object);
135
136
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
137
	gffm->fmd = NULL;
138
}
139
140
static void
141
g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor,
142
    const gchar *dirname, const gchar *basename,
143
    const gchar *filename, GFileMonitorSource *source) {
144
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR (local_monitor);
145
146
	g_assert(NULL != kqueue_fnm);
147
	g_source_ref((GSource*)source);
148
149
	if (NULL == filename) {
150
		filename = dirname;
151
	}
152
	gffm->fmd = kqueue_fnm_add(kqueue_fnm, filename, source);
153
}
154
155
static void
156
g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) {
157
	
158
}
159
160
static void
161
g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) {
162
	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
163
	GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class);
164
165
	class->is_supported = g_kqueue_file_monitor_is_supported;
166
	class->start = g_kqueue_file_monitor_start;
167
	class->mount_notify = TRUE; /* TODO: ??? */
168
	file_monitor_class->cancel = g_kqueue_file_monitor_cancel;
169
	gobject_class->finalize = g_kqueue_file_monitor_finalize;
170
}
171
172
static void
173
g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) {
174
	
175
}
176
177
void
178
g_io_module_load(GIOModule *module) {
179
180
	g_type_module_use(G_TYPE_MODULE(module));
181
182
	g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
183
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
184
	g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
185
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
186
}
187
188
void
189
g_io_module_unload(GIOModule *module) {
190
	
191
	g_assert_not_reached();
192
}
193
194
char **
195
g_io_module_query(void) {
196
	char *eps[] = {
197
		G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
198
		G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
199
		NULL
200
	};
201
202
	return (g_strdupv(eps));
203
}
(-)devel/glib20/files/kqueue_fnm.c.in (+716 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
	size_t		st_blksize;
73
	void		*udata;
74
	kqueue_fnm_p	kfnm;
75
	/* For dir. */
76
	file_info_p	files;
77
	volatile size_t	files_count;
78
	size_t		files_allocated;
79
} kqueue_file_mon_data_t;
80
81
82
typedef struct kqueue_file_nonify_monitor_s {
83
	int		fd;		/* kqueue() fd. */
84
	int		pfd[2];		/* pipe queue specific. */
85
	uint8_t		*tmpbuf;
86
	size_t		tmpbuf_size;
87
} kqueue_fnm_t;
88
89
90
typedef void (*kq_msg_cb)(kqueue_file_mon_data_p fmd);
91
92
typedef struct kqueue_file_mon_msg_pkt_s {
93
	size_t		magic;
94
	kq_msg_cb	msg_cb;
95
	kqueue_file_mon_data_p fmd;
96
	size_t		chk_sum;
97
} kqueue_fnm_msg_pkt_t, *kqueue_fnm_msg_pkt_p;
98
99
#define KF_MSG_PKT_MAGIC	0xffddaa00
100
101
102
#ifndef O_NOATIME
103
#	define O_NOATIME	0
104
#endif
105
#ifndef O_EVTONLY
106
#	define O_EVTONLY	O_RDONLY
107
#endif
108
#define OPEN_FILE_FLAGS		(O_EVTONLY | O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC)
109
110
#define EVFILT_VNODE_FLAGS_ALL	(NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE)
111
112
113
#ifndef _GENERIC_DIRSIZ
114
#	define _GENERIC_DIRSIZ(__de)	MIN((__de)->d_reclen, sizeof(struct dirent))
115
#endif
116
117
#define IS_NAME_DOTS(__name)	('.' == (__name)[0] &&			\
118
				 ('\0' == (__name)[1] || 		\
119
				  ('.' == (__name)[1] && '\0' == (__name)[2])))
120
#define IS_DE_NAME_EQ(__de1, __de2)  (0 == mem_cmpn((__de1)->d_name,	\
121
						    (__de1)->d_namlen,	\
122
						    (__de2)->d_name,	\
123
						    (__de2)->d_namlen))
124
#define zalloc(__size)		calloc(1, (__size))
125
126
127
static inline int
128
mem_cmpn(const void *buf1, const size_t buf1_size,
129
    const void *buf2, const size_t buf2_size) {
130
131
	if (buf1_size != buf2_size)
132
		return (((buf1_size > buf2_size) ? 127 : -127));
133
	if (0 == buf1_size || buf1 == buf2)
134
		return (0);
135
	if (NULL == buf1)
136
		return (-127);
137
	if (NULL == buf2)
138
		return (127);
139
	return (memcmp(buf1, buf2, buf1_size));
140
}
141
142
static inline int
143
realloc_items(void **items, const size_t item_size,
144
    size_t *allocated, const size_t alloc_blk_cnt, const size_t count) {
145
	size_t allocated_prev, allocated_new;
146
	uint8_t *items_new;
147
148
	if (NULL == items || 0 == item_size || NULL == allocated ||
149
	    0 == alloc_blk_cnt)
150
		return (EINVAL);
151
	allocated_prev = (*allocated);
152
	if (NULL != (*items) &&
153
	    allocated_prev > count &&
154
	    allocated_prev <= (count + alloc_blk_cnt))
155
		return (0);
156
	allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt);
157
#if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
158
	items_new = (uint8_t*)reallocarray((*items), item_size, allocated_new);
159
#else /* For old BSD systems. */
160
	items_new = (uint8_t*)realloc((*items), (item_size * allocated_new));
161
#endif
162
	if (NULL == items_new) /* Realloc fail! */
163
		return (ENOMEM);
164
165
	if (allocated_new > allocated_prev) { /* Init new mem. */
166
		bzero((items_new + (allocated_prev * item_size)),
167
		    ((allocated_new - allocated_prev) * item_size));
168
	}
169
	(*items) = items_new;
170
	(*allocated) = allocated_new;
171
172
	return (0);
173
}
174
175
176
static int
177
readdir_data_start(int fd, uint8_t *buf, size_t buf_size, readdir_data_p rdd) {
178
179
	if (-1 == fd || NULL == buf || 0 == buf_size || NULL == rdd)
180
		return (EINVAL);
181
	if (-1 == lseek(fd, 0, SEEK_SET))
182
		return (errno);
183
	bzero(rdd, sizeof(readdir_data_t));
184
	rdd->fd = fd;
185
	rdd->buf = buf;
186
	rdd->buf_size = buf_size;
187
188
	return (0);
189
}
190
191
static int
192
readdir_data_next(readdir_data_p rdd, struct dirent *de) {
193
	int ios;
194
	uint8_t *ptr;
195
196
	if (NULL == rdd || NULL == de)
197
		return (EINVAL);
198
199
retry:
200
	if (rdd->buf_used <= rdd->buf_pos) {
201
		ios = getdents(rdd->fd, (char*)rdd->buf, rdd->buf_size);
202
		if (-1 == ios)
203
			return (errno);
204
		rdd->buf_used = (size_t)ios;
205
		rdd->buf_pos = 0;
206
		if (0 == ios)
207
			return (ESPIPE); /* EOF. */
208
	}
209
	/* Keep data aligned. */
210
	ptr = (rdd->buf + rdd->buf_pos);
211
	memcpy(de, ptr, (sizeof(struct dirent) - sizeof(de->d_name)));
212
	if (0 == de->d_reclen)
213
		return (ESPIPE); /* EOF. */
214
	rdd->buf_pos += de->d_reclen;
215
#ifdef DT_WHT
216
	if (DT_WHT == de->d_type)
217
		goto retry;
218
#endif
219
	memcpy(de, ptr, _GENERIC_DIRSIZ(de));
220
	if (IS_NAME_DOTS(de->d_name))
221
		goto retry;
222
	return (0);
223
}
224
225
226
static int
227
file_info_find_de_fileno(file_info_p files, size_t files_count,
228
    struct dirent *de, size_t *idx) {
229
	size_t i;
230
231
	if (NULL == files || NULL == de || NULL == idx)
232
		return (0);
233
	for (i = 0; i < files_count; i ++) {
234
		if (de->d_type != files[i].de.d_type ||
235
		    de->d_fileno != files[i].de.d_fileno)
236
			continue;
237
		(*idx) = i;
238
		return (1);
239
	}
240
	(*idx) = files_count;
241
	return (0);
242
}
243
244
static int
245
file_info_find_de_name(file_info_p files, size_t files_count,
246
    const size_t skip_idx, struct dirent *de, size_t *idx) {
247
	size_t i;
248
249
	if (NULL == files || NULL == de || NULL == idx)
250
		return (0);
251
	for (i = 0; i < files_count; i ++) {
252
		if (i == skip_idx ||
253
		    de->d_type != files[i].de.d_type ||
254
		    0 == IS_DE_NAME_EQ(de, &files[i].de))
255
			continue;
256
		(*idx) = i;
257
		return (1);
258
	}
259
	(*idx) = files_count;
260
	return (0);
261
}
262
263
264
static void
265
kqueue_file_mon_data_clean(kqueue_file_mon_data_p fmd) {
266
267
	if (NULL == fmd)
268
		return;
269
	close(fmd->fd);
270
	fmd->fd = -1;
271
	fmd->files_count = 0;
272
	fmd->files_allocated = 0;
273
	free(fmd->files);
274
	fmd->files = NULL;
275
}
276
277
static void
278
kqueue_file_mon_data_free(kqueue_file_mon_data_p fmd) {
279
280
	if (NULL == fmd)
281
		return;
282
	close(fmd->fd);
283
	free(fmd->files);
284
	free(fmd);
285
}
286
287
static kqueue_file_mon_data_p
288
kqueue_file_mon_data_alloc(kqueue_fnm_p kfnm, const char *path, void *udata) {
289
	kqueue_file_mon_data_p fmd;
290
291
	if (NULL == kfnm || NULL == path)
292
		return (NULL);
293
	fmd = zalloc(sizeof(kqueue_file_mon_data_t));
294
	if (NULL == fmd)
295
		return (NULL);
296
	/* Remember args. */
297
	fmd->path_size = strlcpy(fmd->path, path, PATH_MAX);
298
	fmd->name_offset = fmd->path_size;
299
	fmd->udata = udata;
300
	fmd->kfnm = kfnm;
301
302
	return (fmd);
303
}
304
305
static int
306
kqueue_file_mon_data_readdir(kqueue_file_mon_data_p fmd) {
307
	int error;
308
	struct dirent *de;
309
	uint8_t *tmpbuf;
310
	readdir_data_t rdd;
311
312
	if (NULL == fmd || 0 == fmd->is_dir)
313
		return (EINVAL);
314
315
	/* Get temp buf. */
316
	if (fmd->st_blksize > fmd->kfnm->tmpbuf_size) {
317
		tmpbuf = realloc(fmd->kfnm->tmpbuf, fmd->st_blksize);
318
		if (NULL == tmpbuf)
319
			return (ENOMEM);
320
		fmd->kfnm->tmpbuf = tmpbuf;
321
		fmd->kfnm->tmpbuf_size = fmd->st_blksize;
322
	}
323
324
	fmd->files_count = 0;
325
	fmd->files_allocated = 0;
326
	free(fmd->files);
327
	fmd->files = NULL;
328
329
	error = readdir_data_start(fmd->fd, fmd->kfnm->tmpbuf, fmd->st_blksize, &rdd);
330
	if (0 != error)
331
		return (error);
332
	for (;;) {
333
		if (0 != realloc_items((void**)&fmd->files,
334
		    sizeof(file_info_t), &fmd->files_allocated,
335
		    FILES_ALLOC_BLK_SIZE, fmd->files_count)) {
336
			fmd->files_count = 0;
337
			fmd->files_allocated = 0;
338
			free(fmd->files);
339
			fmd->files = NULL;
340
			return (ENOMEM);
341
		}
342
		de = &fmd->files[fmd->files_count].de; /* Use short name. */
343
		/* Get file name from folder. */
344
		if (0 != readdir_data_next(&rdd, de))
345
			break;
346
		/* Get file attrs. */
347
		if (0 != fstatat(fmd->fd, de->d_name,
348
		    &fmd->files[fmd->files_count].sb,
349
		    AT_SYMLINK_NOFOLLOW)) {
350
			bzero(&fmd->files[fmd->files_count].sb,
351
			    sizeof(struct stat));
352
		}
353
		fmd->files_count ++;
354
	}
355
356
	return (0); /* OK. */
357
}
358
359
360
static void
361
kqueue_file_mon_data_init(kqueue_file_mon_data_p fmd) {
362
	struct stat sb;
363
	struct kevent kev;
364
	struct timespec ke_timeout;
365
366
	if (NULL == fmd)
367
		return;
368
	fmd->fd = open(fmd->path, OPEN_FILE_FLAGS);
369
	if (-1 == fmd->fd)
370
		return;
371
	if (0 != fstat(fmd->fd, &sb))
372
		goto err_out;
373
	fmd->st_blksize = (size_t)sb.st_blksize;
374
	/* Get parent folder name. */
375
	if (S_ISDIR(sb.st_mode)) {
376
		fmd->is_dir = 1;
377
		/* Be sure that folder contain trailing '/'. */
378
		if ('/' != fmd->path[(fmd->path_size - 1)]) {
379
			fmd->path[fmd->path_size] = '/';
380
			fmd->path_size ++;
381
			fmd->path[fmd->path_size] = 0;
382
		}
383
		/* Skip last '/' for parent dir search. */
384
		fmd->name_offset = (fmd->path_size - 1);
385
	}
386
	/* Find parent dir path size. */
387
	while (0 < fmd->name_offset && '/' != fmd->path[(fmd->name_offset - 1)]) {
388
		fmd->name_offset --;
389
	}
390
391
	/* Dir special processing. */
392
	if (0 != fmd->is_dir) {
393
		/* Read and remember dir content. */
394
		if (0 != kqueue_file_mon_data_readdir(fmd))
395
			goto err_out;
396
	}
397
	/* Add to kqueue. */
398
	bzero(&kev, sizeof(kev));
399
	kev.ident = (uintptr_t)fmd->fd;
400
	kev.filter = EVFILT_VNODE;
401
	kev.flags = (EV_ADD | EV_CLEAR);
402
	kev.fflags = EVFILT_VNODE_FLAGS_ALL;
403
	kev.udata = (void*)fmd;
404
405
	bzero(&ke_timeout, sizeof(ke_timeout));
406
407
	if (-1 == kevent(fmd->kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
408
		goto err_out;
409
	return; /* OK. */
410
411
err_out:
412
	kqueue_file_mon_data_clean(fmd);
413
}
414
415
416
static void
417
kqueue_handle_changes(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd,
418
    kfnm_event_handler_cb cb_func) {
419
	struct stat sb;
420
	size_t i, k, files_count;
421
	file_info_p files;
422
423
	if (NULL == kfnm || NULL == fmd || NULL == cb_func)
424
		return;
425
	if (0 != fstat(fmd->fd, &sb) ||
426
	    0 == sb.st_nlink) {
427
		kqueue_file_mon_data_clean(fmd);
428
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
429
		    fmd->path, "", NULL);
430
		return;
431
	}
432
	if (0 == fmd->is_dir) {
433
		fmd->path[fmd->name_offset] = 0;
434
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED, fmd->path,
435
		    (fmd->path + fmd->name_offset), NULL);
436
		fmd->path[fmd->name_offset] = '/';
437
		return;
438
	}
439
440
	/* Dir processing. */
441
442
	/* Save prev. */
443
	files = fmd->files;
444
	files_count = fmd->files_count;
445
	fmd->files = NULL;
446
	/* Update dir. */
447
	if (0 != kqueue_file_mon_data_readdir(fmd))
448
		goto err_out;
449
	/* Notify removed first. */
450
	for (i = 0; i < files_count; i ++) {
451
		if (0 != file_info_find_de_fileno(fmd->files, fmd->files_count, &files[i].de, &k) ||
452
		    0 != file_info_find_de_name(fmd->files, fmd->files_count, ~0, &files[i].de, &k)) /* Deleted. */
453
			continue;
454
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
455
			    fmd->path, files[i].de.d_name, NULL);
456
	}
457
	/* Notify. */
458
	for (i = 0; i < fmd->files_count; i ++) {
459
		/* Is new file/folder? */
460
		if (0 == file_info_find_de_fileno(files, files_count, &fmd->files[i].de, &k) &&
461
		    0 == file_info_find_de_name(files, files_count, ~0, &fmd->files[i].de, &k)) { /* Add new. */
462
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CREATED,
463
			    fmd->path, fmd->files[i].de.d_name, NULL);
464
			continue;
465
		}
466
		/* Is renamed? */
467
		if (0 == IS_DE_NAME_EQ(&files[k].de, &fmd->files[i].de)) {
468
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
469
			    fmd->path, files[k].de.d_name, fmd->files[i].de.d_name);
470
			continue;
471
		}
472
		/* Is modified? */
473
		if (0 != memcmp(&fmd->files[i].sb, &files[k].sb, sizeof(struct stat))) {
474
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
475
			    fmd->path, fmd->files[i].de.d_name, NULL);
476
			continue;
477
		}
478
		/* Not changed. */
479
	}
480
481
err_out:
482
	free(files);
483
	return;
484
}
485
486
static void
487
kqueue_handle_rename(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd,
488
    kfnm_event_handler_cb cb_func) {
489
	int up_dir_fd, found = 0;
490
	readdir_data_t rdd;
491
	struct dirent de;
492
	struct stat sb;
493
	char old_filename[(MAXNAMLEN + 2)];
494
	size_t old_filename_size;
495
496
	if (NULL == kfnm || NULL == fmd || NULL == cb_func)
497
		return;
498
	if (0 != fstat(fmd->fd, &sb) ||
499
	    0 == sb.st_nlink) {
500
notify_removed:
501
		kqueue_file_mon_data_clean(fmd);
502
		cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
503
		    fmd->path, "", NULL);
504
		return;
505
	}
506
	/* Save old file name. */
507
	old_filename_size = (fmd->path_size - fmd->name_offset - fmd->is_dir);
508
	memcpy(old_filename,
509
	    (fmd->path + fmd->name_offset),
510
	    old_filename_size);
511
	old_filename[old_filename_size] = 0;
512
513
	/* Get parent folder name. */
514
	fmd->path[fmd->name_offset] = 0;
515
	/* Try to open. */
516
	up_dir_fd = open(fmd->path, (OPEN_FILE_FLAGS | O_DIRECTORY));
517
	/* Restore '/' after parent folder. */
518
	fmd->path[fmd->name_offset] = '/';
519
	if (-1 == up_dir_fd ||
520
	    0 != fstat(up_dir_fd, &sb) ||
521
	    0 != readdir_data_start(up_dir_fd, fmd->kfnm->tmpbuf, sb.st_blksize, &rdd)) {
522
		close(up_dir_fd);
523
		return;
524
	}
525
	/* Find new name by inode. */
526
	while (0 == readdir_data_next(&rdd, &de)) {
527
		if (de.d_fileno == sb.st_ino) {
528
			found ++;
529
			break;
530
		}
531
	}
532
	close(up_dir_fd);
533
	if (0 == found)
534
		goto notify_removed; /* Not found. */
535
	/* Update name. */
536
	if (PATH_MAX <= (fmd->name_offset + de.d_namlen))
537
		return; /* Too long. */
538
	memcpy((fmd->path + fmd->name_offset), de.d_name, de.d_namlen);
539
	fmd->path_size = (fmd->name_offset + de.d_namlen);
540
	/* Add last '/' for dir. */
541
	fmd->path[fmd->path_size] = '/';
542
	fmd->path_size += fmd->is_dir;
543
	fmd->path[fmd->path_size] = 0;
544
	/* Notify. */
545
	cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
546
	    fmd->path, old_filename, de.d_name);
547
}
548
549
550
static void
551
kqueue_fnm_delay_call_process(kqueue_fnm_p kfnm, kq_msg_cb forced_msg_cb) {
552
	ssize_t ios;
553
	kqueue_fnm_msg_pkt_t msg;
554
555
	for (;;) {
556
		ios = read(kfnm->pfd[0], &msg, sizeof(msg));
557
		if (0 >= ios)
558
			return;
559
		if (sizeof(msg) != ios ||
560
		    KF_MSG_PKT_MAGIC != msg.magic ||
561
		    (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd)) != msg.chk_sum)
562
			continue;
563
		if (NULL != forced_msg_cb) {
564
			forced_msg_cb(msg.fmd);
565
			continue;
566
		}
567
		if (NULL == msg.msg_cb)
568
			continue;
569
		msg.msg_cb(msg.fmd);
570
	}
571
}
572
573
static int
574
kqueue_fnm_delay_call(kqueue_fnm_p kfnm, kq_msg_cb msg_cb,
575
    kqueue_file_mon_data_p fmd) {
576
	kqueue_fnm_msg_pkt_t msg;
577
578
	if (NULL == kfnm || NULL == fmd)
579
		return (EINVAL);
580
	msg.magic = KF_MSG_PKT_MAGIC;
581
	msg.msg_cb = msg_cb;
582
	msg.fmd = fmd;
583
	msg.chk_sum = (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd));
584
	if (sizeof(msg) == write(kfnm->pfd[1], &msg, sizeof(msg)))
585
		return (0);
586
	return (errno);
587
}
588
589
590
void
591
kqueue_fnm_free(kqueue_fnm_p kfnm) {
592
593
	if (NULL == kfnm)
594
		return;
595
	close(kfnm->fd);
596
	/* Free all in delay calls queue. */
597
	kqueue_fnm_delay_call_process(kfnm, kqueue_file_mon_data_free);
598
	close(kfnm->pfd[0]);
599
	close(kfnm->pfd[1]);
600
	free(kfnm->tmpbuf);
601
	free(kfnm);
602
}
603
604
kqueue_fnm_p
605
kqueue_fnm_create(void) {
606
	kqueue_fnm_p kfnm;
607
	struct kevent kev;
608
	struct timespec ke_timeout;
609
610
	kfnm = zalloc(sizeof(kqueue_fnm_t));
611
	if (NULL == kfnm)
612
		return (NULL);
613
	kfnm->fd = kqueue();
614
	if (-1 == kfnm->fd)
615
		goto err_out;
616
	if (-1 == pipe2(kfnm->pfd, O_NONBLOCK))
617
		goto err_out;
618
619
	bzero(&kev, sizeof(kev));
620
	kev.ident = kfnm->pfd[0];
621
	kev.filter = EVFILT_READ;
622
	kev.flags = (EV_ADD | EV_ENABLE);
623
624
	bzero(&ke_timeout, sizeof(ke_timeout));
625
626
	if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
627
		goto err_out;
628
	return (kfnm);
629
630
err_out:
631
	kqueue_fnm_free(kfnm);
632
	return (NULL);
633
}
634
635
kqueue_file_mon_data_p
636
kqueue_fnm_add(kqueue_fnm_p kfnm, const char *path, void *udata) {
637
	int error;
638
	kqueue_file_mon_data_p fmd;
639
	
640
	if (NULL == kfnm || NULL == path)
641
		return (NULL);
642
	fmd = kqueue_file_mon_data_alloc(kfnm, path, udata);
643
	if (NULL == fmd)
644
		return (NULL);
645
	/* Shedule delay call to init. */
646
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_init, fmd);
647
	if (0 != error) { /* Error, do no directly init to avoid freezes. */
648
		kqueue_file_mon_data_free(fmd);
649
		return (NULL);
650
	}
651
	return (fmd);
652
}
653
654
void
655
kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
656
	int error;
657
658
	if (NULL == kfnm || NULL == fmd)
659
		return;
660
	/* Cancel notifications. */
661
	close(fmd->fd);
662
	fmd->fd = -1;
663
	/* Shedule delay call to free. */
664
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_free, fmd);
665
	if (0 == error)
666
		return;
667
	/* Error, free directly. */
668
	kqueue_file_mon_data_free(fmd);
669
}
670
671
672
int
673
kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm) {
674
675
	if (NULL == kfnm)
676
		return (-1);
677
	return (kfnm->fd);
678
}
679
680
void
681
kqueue_fnm_proccess_events(kqueue_fnm_p kfnm, kfnm_event_handler_cb cb_func) {
682
	struct kevent kev;
683
	struct timespec ke_timeout;
684
	kqueue_file_mon_data_p fmd;
685
686
	if (NULL == kfnm || NULL == cb_func)
687
		return;
688
	/* Get and proccess events. */
689
	bzero(&ke_timeout, sizeof(ke_timeout));
690
	while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, &ke_timeout)) {
691
		if (kev.ident == (uintptr_t)kfnm->pfd[0] &&
692
		    kev.filter == EVFILT_READ) { /* Handle delay calls. */
693
			kqueue_fnm_delay_call_process(kfnm, NULL);
694
			continue;
695
		}
696
		if (EVFILT_VNODE != kev.filter ||
697
		    0 == kev.udata)
698
			continue; /* Unknown event or no associated data, skip. */
699
		fmd = (kqueue_file_mon_data_p)kev.udata;
700
701
		if (EV_ERROR & kev.flags) {
702
			kev.flags |= NOTE_REVOKE; /* Treat error as unmount. */
703
		}
704
		if (NOTE_RENAME & kev.fflags) {
705
			kqueue_handle_rename(kfnm, fmd, cb_func);
706
		}
707
		if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB) & kev.fflags) {
708
			kqueue_handle_changes(kfnm, fmd, cb_func);
709
		}
710
		if ((NOTE_DELETE | NOTE_REVOKE) & kev.fflags) {
711
			kqueue_file_mon_data_clean(fmd);
712
			cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
713
			    fmd->path, "", NULL);
714
		}
715
	}
716
}
(-)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__ */

Return to bug 214338