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