d4d7ddbd27a5da7a50365f9afec0e8d53d973283
[platform/upstream/glib.git] / gio / fen / fen-node.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /* 
4  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
5  * reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Lin Ma <lin.ma@sun.com>
23  */
24
25 #include "config.h"
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <strings.h>
29 #include <glib.h>
30 #include "fen-kernel.h"
31 #include "fen-node.h"
32 #include "fen-dump.h"
33
34 #ifdef GIO_COMPILATION
35 #include "gfilemonitor.h"
36 #else
37 #include "gam_event.h"
38 #include "gam_server.h"
39 #include "gam_protocol.h"
40 #endif
41
42 #ifdef GIO_COMPILATION
43 #define FN_W if (fn_debug_enabled) g_debug
44 static gboolean fn_debug_enabled = FALSE;
45 #else
46 #include "gam_error.h"
47 #define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
48 #endif
49
50 G_LOCK_EXTERN (fen_lock);
51
52 /* Must continue monitoring if:
53  * 1) I'm subscribed,
54  * 2) The subscribed children (one of the children has subs) are missing,
55  * 3) my parent is subscribed (monitoring directory).
56  */
57 #define NODE_NEED_MONITOR(f)                                            \
58     (NODE_IS_ACTIVE(f) || node_children_num(f) > 0 || NODE_IS_REQUIRED_BY_PARENT(f))
59
60 static int concern_events[] = {
61     FILE_DELETE,
62     FILE_RENAME_FROM,
63     UNMOUNTED,
64     MOUNTEDOVER,
65 #ifdef GIO_COMPILATION
66     FILE_MODIFIED,
67     FILE_ATTRIB,
68 #else
69     FILE_MODIFIED | FILE_ATTRIB,
70 #endif
71     FILE_RENAME_TO,
72 };
73
74 node_t *ROOT = NULL;
75
76 static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event);
77 static void node_emit_events(node_t *f, const node_event_t *ne);
78 static int node_event_translate(int event, gboolean pair);
79 static void node_add_event (node_t *f, node_event_t *ev);
80 static node_t* node_new (node_t* parent, const gchar* basename);
81 static void node_delete (node_t* parent);
82 static node_t* node_get_child (node_t *f, const gchar *basename);
83 static void children_add (node_t *p, node_t *f);
84 static void children_remove (node_t *p, node_t *f);
85 static gboolean children_remove_cb (gpointer key, gpointer value, gpointer user_data);
86 static guint node_children_num (node_t *f);
87
88 gboolean
89 node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2)
90 {
91     if (val1->tv_sec < val2->tv_sec)
92         return TRUE;
93   
94     if (val1->tv_sec > val2->tv_sec)
95         return FALSE;
96   
97     /* val1->tv_sec == val2->tv_sec */
98     if (val1->tv_usec < val2->tv_usec)
99         return TRUE;
100   
101     return FALSE;
102 }
103
104 void
105 node_traverse (node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data)
106 {
107     GHashTableIter iter;
108     gpointer value;
109
110     g_assert(traverse_cb);
111     if (node == NULL) {
112         node = ROOT;
113     }
114
115     if (node) {
116         traverse_cb(node, user_data);
117     }
118
119     g_hash_table_iter_init (&iter, node->children);
120     while (g_hash_table_iter_next (&iter, NULL, &value)) {
121         node_traverse((node_t *)value, traverse_cb, user_data);
122     }
123 }
124
125 node_t*
126 node_find(node_t* node, const gchar* filename, gboolean create_on_missing)
127 {
128     gchar* str;
129     gchar* token;
130     gchar* lasts;
131     node_t* parent;
132     node_t* child;
133     
134     g_assert (filename && filename[0] == '/');
135
136     if (node == NULL) {
137         node = ROOT;
138     }
139     
140     FN_W ("%s %s\n", __func__, filename);
141
142     parent = child = node;
143     str = g_strdup (filename);
144     
145     for (token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts);
146          token != NULL && child != NULL;
147          token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) {
148         FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
149         child = node_get_child(parent, token);
150         if (child) {
151             parent = child;
152         } else if (create_on_missing) {
153             child = node_new (parent, token);
154             if (child) {
155                 children_add (parent, child);
156                 parent = child;
157                 continue;
158             } else {
159                 FN_W ("%s create %s failed", __func__, token);
160             }
161         } else {
162             break;
163         }
164     }
165     
166     g_free (str);
167     return child;
168 }
169
170 gint
171 node_lstat(node_t *f)
172 {
173     struct stat buf;
174
175     g_assert(!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED));
176
177     if (lstat(NODE_NAME(f), &buf) == 0) {
178         FN_W ("%s %s\n", __func__, NODE_NAME(f));
179         FILE_OBJECT(f)->fo_atime = buf.st_atim;
180         FILE_OBJECT(f)->fo_mtime = buf.st_mtim;
181         FILE_OBJECT(f)->fo_ctime = buf.st_ctim;
182         NODE_SET_FLAG(f, NODE_FLAG_STAT_UPDATED |
183           (S_ISDIR (buf.st_mode) ? NODE_FLAG_DIR : NODE_FLAG_NONE));
184         return 0;
185     } else {
186         FN_W ("%s(lstat) %s %s\n", __func__, NODE_NAME(f), g_strerror (errno));
187     }
188     return errno;
189 }
190
191 void
192 node_create_children_snapshot(node_t *f, gint created_event, gboolean emit)
193 {
194         GDir *dir;
195         GError *err = NULL;
196     
197     FN_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
198
199     dir = g_dir_open (NODE_NAME(f), 0, &err);
200     if (dir) {
201         const char *basename;
202         node_t *child = NULL;
203         
204         while ((basename = g_dir_read_name (dir))) {
205             node_t* data;
206             GList *idx;
207
208             child = node_get_child (f, basename);
209             if (child == NULL) {
210                 gchar *filename;
211             
212                 child = node_new (f, basename);
213                 children_add (f, child);
214             }
215
216             if (f->dir_subs) {
217                 /* We need monitor the new children, or the existed child which
218                  * is in the DELETED mode.
219                  */
220                 if (!NODE_HAS_STATE(child, NODE_STATE_ASSOCIATED) &&
221                   node_lstat(child) == 0 && port_add(child) == 0) {
222                     if (emit) {
223                         /* Emit the whatever event for the new found file. */
224                         node_emit_one_event(child, child->dir_subs, NULL, created_event);
225                         node_emit_one_event(child, child->subs, NULL, created_event);
226                         node_emit_one_event(child, f->dir_subs, NULL, created_event);
227                         node_emit_one_event(child, f->subs, NULL, created_event);
228                     }
229                 }
230                 /* else ignore, because it may be deleted. */
231             }
232         }
233         g_dir_close (dir);
234
235         /* We have finished children snapshot. Any other new added subs should
236          * directory iterate the snapshot instead of scan directory again.
237          */
238         NODE_SET_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED);
239
240     } else {
241         FN_W (err->message);
242         g_error_free (err);
243     }
244 }
245
246 /**
247  * If all active children nodes are ported, then cancel monitor the parent
248  * node. If we know how many children are created, then we can stop accordingly.
249  *
250  * Unsafe, need lock. 
251  */
252 static void
253 foreach_known_children_scan(gpointer key, gpointer value, gpointer user_data)
254 {
255     node_t* f = (node_t*)value;
256     
257     FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
258
259     if (!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) {
260         if (node_lstat(f) == 0 && port_add(f) == 0) {
261             node_emit_one_event(f, f->dir_subs, NULL, FN_EVENT_CREATED);
262             node_emit_one_event(f, f->subs, NULL, FN_EVENT_CREATED);
263             if (NODE_PARENT(f)) {
264                 node_emit_one_event(f, NODE_PARENT(f)->dir_subs, NULL, FN_EVENT_CREATED);
265                 node_emit_one_event(f, NODE_PARENT(f)->subs, NULL, FN_EVENT_CREATED);
266             }
267         }
268     }
269 }
270
271 gboolean
272 node_try_delete(node_t* node)
273 {
274     g_assert (node);
275
276     FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
277
278     /* Try clean children */
279     if (node_children_num (node) > 0) {
280         g_hash_table_foreach_remove(node->children, children_remove_cb, NULL);
281     }
282     if (!NODE_NEED_MONITOR(node)) {
283         /* Clean some flags. */
284         /* NODE_CLE_FLAG(node, NODE_FLAG_HAS_SNAPSHOT | NODE_FLAG_STAT_DONE); */
285         node->flag = 0;
286
287         /* Now we handle the state. */
288         if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED)) {
289             port_remove(node);
290         }
291         /* Actually ignore the ROOT node. */
292         if (node->state == 0 && NODE_PARENT(node)) {
293             children_remove(NODE_PARENT(node), node);
294             /* Do clean instead of returning TRUE. */
295             node_delete (node);
296         }
297         /* else, we have events, clean event queue? */
298     }
299     return FALSE;
300 }
301
302 static node_t*
303 node_new (node_t* parent, const gchar* basename)
304 {
305         node_t *f = NULL;
306
307     g_assert (basename && basename[0]);
308
309     if ((f = g_new0(node_t, 1)) != NULL) {
310         if (parent) {
311             NODE_NAME(f) = g_build_filename(NODE_NAME(parent), basename, NULL);
312         } else {
313             NODE_NAME(f) = g_strdup(G_DIR_SEPARATOR_S);
314         }
315         f->basename = g_strdup (basename);
316         /* f->children = g_hash_table_new_full (g_str_hash, g_str_equal, */
317         /*   NULL, (GDestroyNotify)node_delete); */
318         f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
319           NULL, NULL);
320 #ifdef GIO_COMPILATION
321         f->gfile = g_file_new_for_path (NODE_NAME(f));
322 #endif
323         FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
324     }
325         return f;
326 }
327
328 static void
329 node_delete (node_t *f)
330 {
331     FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
332     g_assert(f->state == 0);
333     g_assert(!NODE_IS_ACTIVE(f));
334     g_assert(g_hash_table_size (f->children) == 0);
335     g_assert(NODE_PARENT(f) == NULL);
336     g_hash_table_unref(f->children);
337 #ifdef GIO_COMPILATION
338     g_object_unref (f->gfile);
339 #endif
340     g_free(f->basename);
341     g_free(NODE_NAME(f));
342     g_free (f);
343 }
344
345 static void
346 children_add (node_t *p, node_t *f)
347 {
348     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
349     g_hash_table_insert (p->children, f->basename, f);
350     NODE_PARENT(f) = p;
351 }
352
353 static void
354 children_remove (node_t *p, node_t *f)
355 {
356     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
357     g_hash_table_steal (p->children, f->basename);
358     NODE_PARENT(f) = NULL;
359 }
360
361 static node_t *
362 node_get_child (node_t *f, const gchar *basename)
363 {
364     if (f->children) {
365         return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
366     }
367     return NULL;
368 }
369
370 static guint
371 node_children_num (node_t *f)
372 {
373     return g_hash_table_size (f->children);
374 }
375
376 /**
377  * depth first delete recursively
378  */
379 static gboolean
380 children_remove_cb (gpointer key, gpointer value, gpointer user_data)
381 {
382     return node_try_delete ((node_t*)value);
383 }
384
385 gboolean
386 node_class_init()
387 {
388     ROOT = node_new (NULL, G_DIR_SEPARATOR_S);
389     if (ROOT == NULL) {
390         FN_W ("[node] Create ROOT node failed.\n");
391         return FALSE;
392     }
393
394     return port_class_init (node_add_event);
395 }
396
397 /**
398  * Adjust self on failing to Port
399  */
400 void
401 node_adjust_deleted(node_t* f)
402 {
403     node_t *ancestor;
404
405     FN_W ("%s %s\n", __func__, NODE_NAME(f));
406
407     for (ancestor = NODE_PARENT(f);
408          ancestor != NULL;
409          ancestor = NODE_PARENT(ancestor)) {
410         /* Stop if we find a node which been already associated or is existed
411          * and can be associated.
412          */
413         if (NODE_HAS_STATE(ancestor, NODE_STATE_ASSOCIATED) ||
414           (node_lstat(ancestor) == 0 && port_add(ancestor) == 0)) {
415             break;
416         }
417     }
418
419     /* We assume we shouldn't reach here, because Root is always existed and
420      * associated. But given bugster#6955199, if PORT FS has problems on root,
421      * we may reach here. So just return ROOT and the whole GIO fen backend will
422      * fail.
423      */
424     /* g_assert(ancestor != NULL); */
425 }
426
427
428 static void
429 node_emit_events(node_t *f, const node_event_t *ne)
430 {
431     gsize num = sizeof(concern_events)/sizeof(int);
432     gint i;
433     int translated_e;
434     node_t *p;
435
436     if (node_timeval_lt(&f->atv, &ne->ctv)) {
437         int event = ne->e;
438
439         /* Emit DELETED on the pair_data */
440         if (ne->pair_data) {
441             node_t *from = ne->pair_data;
442             node_emit_one_event(from, from->dir_subs, NULL, node_event_translate(FILE_DELETE, FALSE));
443             node_emit_one_event(from, from->subs, NULL, node_event_translate(FILE_DELETE, FALSE));
444         }
445
446         for (i = 0; i < num; i++) {
447             if (event & concern_events[i]) {
448                 translated_e = node_event_translate(concern_events[i], FALSE);
449                 /* Neither GIO or gamin cares about modified events on a
450                  * directory.
451                  */
452 #ifdef GIO_COMPILATION
453                 if ((concern_events[i] & FILE_MODIFIED) == 0) {
454                     node_emit_one_event(f, f->dir_subs, NULL, translated_e);
455                 }
456 #else
457                 /* Gamin doesn't care about attrib changed events on a directory
458                  * either.
459                  */
460                 if ((concern_events[i] & (FILE_MODIFIED | FILE_ATTRIB)) == 0) {
461                     node_emit_one_event(f, f->dir_subs, NULL, translated_e);
462                 }
463 #endif
464                 node_emit_one_event(f, f->subs, NULL, translated_e);
465             }
466             event &= ~concern_events[i];
467         }
468     }
469
470     p = NODE_PARENT(f);
471     if (p != NULL && node_timeval_lt(&p->atv, &ne->ctv)) {
472         int event = ne->e;
473         for (i = 0; i < num; i++) {
474             if (event & concern_events[i]) {
475                 translated_e = node_event_translate(concern_events[i], ne->pair_data != NULL);
476                 node_emit_one_event(f, p->dir_subs, ne->pair_data, translated_e);
477                 node_emit_one_event(f, p->subs, ne->pair_data, translated_e);
478             }
479             event &= ~concern_events[i];
480         }
481     }
482 }
483
484 /**
485  * node_add_event:
486  *
487  */
488 static void
489 node_add_event (node_t *f, node_event_t *ev)
490 {
491     FN_W ("%s %d\n", __func__, ev->e);
492
493     /* Clean the events flag early, because all received events need be
494      * processed in this function.
495      */
496     NODE_CLE_STATE(f, NODE_STATE_HAS_EVENTS);
497
498     /*
499      * Node the node has been created, so we can delete create event in
500      * optimizing. To reduce the statings, we add it to Port on discoving
501      * it then emit CREATED event. So we don't need to do anything here.
502      */
503     if (NODE_NEED_MONITOR(f)) {
504         if (HAS_NO_EXCEPTION_EVENTS(ev->e)) {
505             if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || port_add(f) == 0) {
506                 if ((ev->e & FILE_MODIFIED) && NODE_HAS_FLAG(f, NODE_FLAG_DIR)) {
507                     if (f->dir_subs) {
508                         node_create_children_snapshot(f, FN_EVENT_CREATED, TRUE);
509                     } else {
510                         g_hash_table_foreach(f->children, foreach_known_children_scan, NULL);
511                     }
512                 }
513             } else {
514                 /* Emit delete event */
515                 ev->e |= FILE_DELETE;
516
517                 node_adjust_deleted(f);
518             }
519
520         } else {
521             node_adjust_deleted(f);
522         }
523
524         /* Send events to clients. */
525         node_emit_events (f, ev);
526         
527     } else {
528         /* Send events to clients. */
529         node_emit_events (f, ev);
530
531         node_try_delete(f);
532     }
533
534     if (ev->pair_data) {
535         node_t *from = ev->pair_data;
536         g_assert(ev->e == FILE_RENAME_TO);
537
538         if (NODE_NEED_MONITOR(from)) {
539             /* Clean the events flag, since it may block free this node. */
540             NODE_CLE_STATE(from, NODE_STATE_HAS_EVENTS);
541             node_adjust_deleted(from);
542         } else {
543             node_try_delete(from);
544         }
545     }
546
547     node_event_delete (ev);
548 }
549
550 static void
551 node_emit_one_event(node_t *f, GList *subs, node_t *other, int event)
552 {
553     GList* idx;
554     
555     FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event);
556
557 #ifdef GIO_COMPILATION
558     for (idx = subs; idx; idx = idx->next) {
559         g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile,
560           (other == NULL ? NULL : other->gfile), event);
561     }
562 #else
563     for (idx = subs; idx; idx = idx->next) {
564         gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1);
565     }
566 #endif
567 }
568
569 static int
570 node_event_translate(int event, gboolean pair)
571 {
572 #ifdef GIO_COMPILATION
573     switch (event) {
574     case FILE_DELETE:
575     case FILE_RENAME_FROM:
576         return G_FILE_MONITOR_EVENT_DELETED;
577     case UNMOUNTED:
578         return G_FILE_MONITOR_EVENT_UNMOUNTED;
579     case FILE_ATTRIB:
580         return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
581     case MOUNTEDOVER:
582     case FILE_MODIFIED:
583         return G_FILE_MONITOR_EVENT_CHANGED;
584     case FILE_RENAME_TO:
585         if (pair) {
586             return G_FILE_MONITOR_EVENT_MOVED;
587         } else {
588             return G_FILE_MONITOR_EVENT_CREATED;
589         }
590     default:
591         /* case FILE_ACCESS: */
592         g_assert_not_reached ();
593         return -1;
594     }
595 #else
596     switch (event) {
597     case FILE_DELETE:
598     case FILE_RENAME_FROM:
599         return GAMIN_EVENT_DELETED;
600     case MOUNTEDOVER:
601     case UNMOUNTED:
602         return GAMIN_EVENT_CHANGED;
603     case FILE_RENAME_TO:
604         if (pair) {
605             return GAMIN_EVENT_MOVED;
606         } else {
607             return GAMIN_EVENT_CREATED;
608         }
609     default:
610         if (event & (FILE_ATTRIB | FILE_MODIFIED)) {
611             return GAMIN_EVENT_CHANGED;
612         }
613         /* case FILE_ACCESS: */
614         g_assert_not_reached ();
615         return -1;
616     }
617 #endif
618 }
619
620 node_event_t*
621 node_event_new (int event, gpointer user_data)
622 {
623     node_event_t *ev;
624     
625     if ((ev = g_new (node_event_t, 1)) != NULL) {
626         g_assert (ev);
627         ev->e = event;
628         ev->user_data = user_data;
629         ev->pair_data = NULL;   /* For renamed file. */
630         /* Created timestamp */
631         g_get_current_time(&ev->ctv);
632         ev->rename_tv = ev->ctv;
633     }
634     return ev;
635 }
636
637 void
638 node_event_delete (node_event_t* ev)
639 {
640     g_free (ev);
641 }
642