2 * Copyright (c) 2012, 2013 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <sys/types.h>
19 #include <sys/syscall.h>
22 #include <glib/gprintf.h>
23 #include "mtp_thread.h"
24 #include "mtp_inoti_handler.h"
25 #include "mtp_event_handler.h"
26 #include "mtp_support.h"
27 #include "mtp_device.h"
31 * GLOBAL AND STATIC VARIABLES
33 pthread_mutex_t g_cmd_inoti_mutex;
35 #ifdef MTP_SUPPORT_OBJECTADDDELETE_EVENT
36 mtp_char g_last_created_dir[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
37 mtp_char g_last_deleted[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
38 mtp_char g_last_moved[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
39 mtp_char g_last_copied[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
40 static pthread_t g_inoti_thrd;
41 static mtp_int32 g_cnt_watch_folder = 0;
42 static mtp_int32 g_inoti_fd;
43 static open_files_info_t *g_open_files_list;
44 static inoti_watches_t g_inoti_watches[INOTI_FOLDER_COUNT_MAX];
45 #endif /*MTP_SUPPORT_OBJECTADDDELETE_EVENT*/
50 #ifdef MTP_SUPPORT_OBJECTADDDELETE_EVENT
51 static mtp_bool __process_inoti_event(struct inotify_event *event);
52 static void __remove_inoti_watch(mtp_char *path);
53 static mtp_bool __add_file_to_inoti_open_files_list(mtp_int32 wd,
54 mtp_char *event_name);
55 static open_files_info_t *__find_file_in_inoti_open_files_list(mtp_int32 wd,
56 mtp_char *event_name);
57 static void __remove_file_from_inoti_open_files_list(open_files_info_t *node);
58 static mtp_int32 __get_inoti_watch_id(mtp_int32 iwd);
59 static mtp_bool __get_inoti_event_full_path(mtp_int32 wd, mtp_char *event_name,
60 mtp_char *path, mtp_int32 path_len, mtp_char *parent_path);
61 static void __remove_recursive_inoti_watch(mtp_char *path);
62 static void __clean_up_inoti(void *data);
63 static void __delete_children_from_store_inoti(mtp_store_t *store,
65 static void __process_object_added_event(mtp_char *fullpath,
66 mtp_char *file_name, mtp_char *parent_path);
67 static void __process_object_deleted_event(mtp_char *fullpath,
68 mtp_char *file_name, mtp_bool isdir);
69 static void __destroy_inoti_open_files_list();
70 #endif /* MTP_SUPPORT_OBJECTADDDELETE_EVENT */
75 #ifdef MTP_SUPPORT_OBJECTADDDELETE_EVENT
76 void *_thread_inoti(void *arg)
81 mtp_char buffer[INOTI_BUF_LEN] = { 0 };
82 struct inotify_event *event = NULL;
84 pthread_cleanup_push(__clean_up_inoti, NULL);
86 DBG("START INOTIFY SYSTEM");
92 length = read(g_inoti_fd, buffer, sizeof(buffer));
100 event = (struct inotify_event *)(&buffer[i]);
101 __process_inoti_event(event);
102 temp_idx = i + event->len + INOTI_EVENT_SIZE;
103 if (temp_idx > length)
110 DBG("Inoti thread exited");
111 pthread_cleanup_pop(1);
116 void _inoti_add_watch_for_fs_events(mtp_char *path)
120 ret_if(path == NULL);
122 if (g_cnt_watch_folder == INOTI_FOLDER_COUNT_MAX) {
123 /* find empty cell */
124 for (i = 0; i < INOTI_FOLDER_COUNT_MAX; i++) {
126 if (g_inoti_watches[i].wd != 0)
132 if (i == INOTI_FOLDER_COUNT_MAX) {
133 ERR("no empty space for a new inotify watch.");
136 DBG("g_watch_folders[%d] add watch : %s\n", i, path);
137 g_inoti_watches[i].forlder_name = g_strdup(path);
138 g_inoti_watches[i].wd = inotify_add_watch(g_inoti_fd,
139 g_inoti_watches[i].forlder_name,
140 IN_CLOSE_WRITE | IN_CREATE |
141 IN_DELETE | IN_MOVED_FROM |
146 DBG("g_watch_folders[%d] add watch : %s\n", g_cnt_watch_folder, path);
147 g_inoti_watches[g_cnt_watch_folder].forlder_name = g_strdup(path);
148 g_inoti_watches[g_cnt_watch_folder].wd = inotify_add_watch(g_inoti_fd,
149 g_inoti_watches[g_cnt_watch_folder].forlder_name,
151 IN_CREATE | IN_DELETE |
154 g_cnt_watch_folder++;
159 mtp_bool _inoti_init_filesystem_evnts()
161 mtp_bool ret = FALSE;
163 g_inoti_fd = inotify_init();
164 if (g_inoti_fd < 0) {
165 ERR("inotify_init() Fail : g_inoti_fd = %d", g_inoti_fd);
169 ret = _util_thread_create(&g_inoti_thrd, "File system inotify thread\n",
170 PTHREAD_CREATE_JOINABLE, _thread_inoti, NULL);
172 ERR("_util_thread_create() Fail");
181 static mtp_bool __process_inoti_event(struct inotify_event *event)
183 static mtp_int32 last_moved_cookie = -1;
185 mtp_bool res = FALSE;
186 mtp_char full_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
187 mtp_char parentpath[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
189 if (event->len == 0 || event->len > MTP_MAX_FILENAME_SIZE) {
190 ERR_SECURE("Event len is invalid[%d], event->name[%s]\n", event->len,
193 } else if (event->wd < 1) {
194 ERR("invalid wd : %d\n", event->wd);
198 /* start of one event */
199 res = __get_inoti_event_full_path(event->wd, event->name, full_path,
200 sizeof(full_path), parentpath);
202 ERR("__get_inoti_event_full_path() Fail");
206 if (_util_is_path_len_valid(full_path) == FALSE) {
207 ERR("path len is invalid");
210 DBG_SECURE("Event full path = %s\n", full_path);
211 if (event->mask & IN_MOVED_FROM) {
212 if (!g_strcmp0(g_last_moved, full_path)) {
213 /* Ignore this case as this is generated due to MTP*/
214 DBG("[%s] is moved_from by MTP\n", full_path);
215 memset(g_last_moved, 0,
216 MTP_MAX_PATHNAME_SIZE + 1);
217 last_moved_cookie = event->cookie;
218 } else if (event->mask & IN_ISDIR) {
219 DBG("IN_MOVED_FROM --> IN_ISDIR");
220 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
221 __process_object_deleted_event(full_path,
223 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
225 DBG("IN_MOVED_FROM --> NOT IN_ISDIR");
226 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
227 __process_object_deleted_event(full_path,
229 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
231 } else if (event->mask & IN_MOVED_TO) {
232 DBG("Moved To event, path = [%s]\n", full_path);
233 if (last_moved_cookie == event->cookie) {
234 /* Ignore this case as this is generated due to MTP*/
235 DBG("%s is moved_to by MTP\n", full_path);
236 last_moved_cookie = -1;
238 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
239 __process_object_added_event(full_path,
240 event->name, parentpath);
241 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
243 } else if (event->mask & IN_CREATE) {
244 if (event->mask & IN_ISDIR) {
245 DBG("IN_CREATE --> IN_ISDIR");
246 if (!g_strcmp0(g_last_created_dir, full_path)) {
247 /* Ignore this case as this is generated due to MTP*/
248 DBG("%s folder is generated by MTP\n",
250 memset(g_last_created_dir, 0,
251 MTP_MAX_PATHNAME_SIZE + 1);
253 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
254 __process_object_added_event(full_path,
255 event->name, parentpath);
256 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
259 if (FALSE == __add_file_to_inoti_open_files_list(event->wd,
261 DBG_SECURE("__add_file_to_inoti_open_files_list fail\
264 DBG("IN_CREATE --> NOT IN_ISDIR");
266 } else if (event->mask & IN_DELETE) {
267 if (!g_strcmp0(g_last_deleted, full_path)) {
268 /* Ignore this case as this is generated due to MTP*/
269 DBG("%s is deleted by MTP\n", full_path);
270 memset(g_last_deleted, 0,
271 MTP_MAX_PATHNAME_SIZE + 1);
272 } else if (event->mask & IN_ISDIR) {
273 DBG("IN_DELETE --> IN_ISDIR");
274 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
275 __process_object_deleted_event(full_path,
277 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
279 DBG("IN_DELETE --> NOT IN_ISDIR");
280 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
281 __process_object_deleted_event(full_path,
283 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
285 } else if (event->mask & IN_CLOSE_WRITE) {
286 DBG_SECURE("IN_CLOSE_WRITE %d, %s\n", event->wd, event->name);
287 if (!g_strcmp0(g_last_copied, full_path)) {
288 /* Ignore this case as this is generated due to MTP*/
289 DBG("[%s] is copied by MTP\n", full_path);
290 memset(g_last_copied, 0,
291 MTP_MAX_PATHNAME_SIZE + 1);
293 open_files_info_t *node = NULL;
294 node = __find_file_in_inoti_open_files_list(event->wd,
298 UTIL_LOCK_MUTEX(&g_cmd_inoti_mutex);
299 __process_object_added_event(full_path,
300 event->name, parentpath);
301 UTIL_UNLOCK_MUTEX(&g_cmd_inoti_mutex);
302 __remove_file_from_inoti_open_files_list(node);
306 DBG("This case is ignored");
313 void _inoti_deinit_filesystem_events()
315 if (TRUE != _util_thread_cancel(g_inoti_thrd)) {
316 ERR("thread cancel fail.");
320 if (_util_thread_join(g_inoti_thrd, 0) == FALSE)
321 ERR("_util_thread_join() Fail");
326 static void __remove_inoti_watch(mtp_char *path)
330 for (i = 0; i < g_cnt_watch_folder; i++) {
331 if (g_inoti_watches[i].forlder_name == NULL)
334 if (g_strcmp0(g_inoti_watches[i].forlder_name, path) != 0)
337 g_free(g_inoti_watches[i].forlder_name);
338 g_inoti_watches[i].forlder_name = NULL;
339 inotify_rm_watch(g_inoti_fd, g_inoti_watches[i].wd);
340 g_inoti_watches[i].wd = 0;
345 if (i == g_cnt_watch_folder)
346 ERR("Path not found in g_noti_watches");
350 static mtp_bool __add_file_to_inoti_open_files_list(mtp_int32 wd,
351 mtp_char *event_name)
353 open_files_info_t *new_node = NULL;
355 new_node = (open_files_info_t *)g_malloc(sizeof(open_files_info_t));
356 if (NULL == new_node) {
357 ERR("new_node is null malloc fail");
361 new_node->name = g_strdup(event_name);
364 /* First created file */
365 if (NULL == g_open_files_list) {
366 new_node->previous = NULL;
368 g_open_files_list->next = new_node;
369 new_node->previous = g_open_files_list;
371 new_node->next = NULL;
372 g_open_files_list = new_node;
377 static open_files_info_t *__find_file_in_inoti_open_files_list(mtp_int32 wd,
378 mtp_char *event_name)
380 open_files_info_t *current_node = g_open_files_list;
382 while (NULL != current_node) {
383 if ((current_node->wd == wd) &&
384 (g_strcmp0(current_node->name, event_name) == 0)) {
388 current_node = current_node->previous;
391 ERR("Cannot find file in open file's list");
395 static void __remove_file_from_inoti_open_files_list(open_files_info_t *node)
397 if (NULL != node->previous)
398 node->previous->next = node->next;
400 if (NULL != node->next)
401 node->next->previous = node->previous;
403 if (node == g_open_files_list)
404 g_open_files_list = node->previous;
412 static mtp_int32 __get_inoti_watch_id(mtp_int32 iwd)
416 for (i = 0; i < INOTI_FOLDER_COUNT_MAX; i++) {
417 if (iwd == g_inoti_watches[i].wd)
421 if (i >= INOTI_FOLDER_COUNT_MAX) {
422 ERR("inoti_folder is not found");
429 static mtp_bool __get_inoti_event_full_path(mtp_int32 wd, mtp_char *event_name,
430 mtp_char *path, mtp_int32 path_len, mtp_char *parent_path)
432 mtp_int32 inoti_id = 0;
434 retv_if(wd == 0, FALSE);
435 retv_if(path == NULL, FALSE);
436 retv_if(event_name == NULL, FALSE);
438 inoti_id = __get_inoti_watch_id(wd);
440 ERR("FAIL to find last_inoti_id : %d\n", inoti_id);
444 /* 2 is for / and null character */
445 if (path_len < (strlen(g_inoti_watches[inoti_id].forlder_name) +
446 strlen(event_name) + 2))
449 g_snprintf(path, path_len, "%s/%s",
450 g_inoti_watches[inoti_id].forlder_name, event_name);
451 g_snprintf(parent_path, path_len, "%s",
452 g_inoti_watches[inoti_id].forlder_name);
457 static void __remove_recursive_inoti_watch(mtp_char *path)
460 mtp_char *res = NULL;
462 for (i = 0; i < g_cnt_watch_folder; i++) {
463 if (g_inoti_watches[i].forlder_name == NULL)
466 res = strstr(g_inoti_watches[i].forlder_name, path);
470 g_free(g_inoti_watches[i].forlder_name);
471 g_inoti_watches[i].forlder_name = NULL;
472 inotify_rm_watch(g_inoti_fd, g_inoti_watches[i].wd);
473 g_inoti_watches[i].wd = 0;
479 static void __clean_up_inoti(void *data)
481 char ext_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
482 char inter_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
483 _util_get_external_path(ext_path);
484 _util_get_internal_path(inter_path);
486 __remove_recursive_inoti_watch((mtp_char *)inter_path);
487 __remove_recursive_inoti_watch((mtp_char *)ext_path);
488 __destroy_inoti_open_files_list();
495 static void __delete_children_from_store_inoti(mtp_store_t *store,
499 ptp_array_t child_arr = { 0 };
500 mtp_obj_t *child_obj = NULL;
501 slist_node_t *node = NULL;
503 __remove_inoti_watch(obj->file_path);
505 _prop_init_ptparray(&child_arr, UINT32_TYPE);
506 _entity_get_child_handles(store, obj->obj_handle, &child_arr);
508 for (i = 0; i < child_arr.num_ele; i++) {
510 mtp_uint32 *ptr32 = child_arr.array_entry;
511 child_obj = _entity_get_object_from_store(store, ptr32[i]);
513 if (child_obj != NULL && child_obj->obj_info != NULL &&
514 child_obj->obj_info->obj_fmt ==
515 PTP_FMT_ASSOCIATION) {
516 __delete_children_from_store_inoti(store, child_obj);
519 node = _util_delete_node(&(store->obj_list), child_obj);
521 _entity_dealloc_mtp_obj(child_obj);
524 _prop_deinit_ptparray(&child_arr);
528 static void __process_object_added_event(mtp_char *fullpath,
529 mtp_char *file_name, mtp_char *parent_path)
531 mtp_uint32 store_id = 0;
532 mtp_store_t *store = NULL;
533 mtp_obj_t *parent_obj = NULL;
534 mtp_uint32 h_parent = 0;
535 mtp_obj_t *obj = NULL;
536 struct stat stat_buf = { 0 };
538 dir_entry_t dir_info = { { 0 }, 0 };
540 if (NULL != g_strrstr(file_name, MTP_TEMP_FILE)) {
541 ERR("File is a temp file");
545 if (file_name[0] == '.') {
546 DBG_SECURE("Hidden file filename=[%s]\n", file_name);
550 store_id = _entity_get_store_id_by_path(fullpath);
551 store = _device_get_store(store_id);
553 ERR("store is NULL so return");
556 parent_obj = _entity_get_object_from_store_by_path(store, parent_path);
557 if (NULL == parent_obj) {
558 char ext_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
559 char inter_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
560 _util_get_external_path(ext_path);
561 _util_get_internal_path(inter_path);
563 if (!g_strcmp0(parent_path, inter_path) ||
564 !g_strcmp0(parent_path, ext_path)) {
565 DBG("parent is the root folder");
568 DBG("Cannot find the parent, return");
572 h_parent = parent_obj->obj_handle;
575 ret = stat(fullpath, &stat_buf);
582 g_strlcpy(dir_info.filename, fullpath, MTP_MAX_PATHNAME_SIZE + 1);
583 dir_info.attrs.mtime = stat_buf.st_mtime;
584 dir_info.attrs.fsize = (mtp_uint64)stat_buf.st_size;
586 /* Reset the attributes */
587 dir_info.attrs.attribute = MTP_FILE_ATTR_MODE_NONE;
588 if (S_ISBLK(stat_buf.st_mode) || S_ISCHR(stat_buf.st_mode) ||
589 S_ISLNK(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode)) {
590 dir_info.attrs.attribute |= MTP_FILE_ATTR_MODE_SYSTEM;
593 if (S_ISREG(stat_buf.st_mode)) {
594 dir_info.type = MTP_FILE_TYPE;
595 dir_info.attrs.attribute |= MTP_FILE_ATTR_MODE_NONE;
597 if (!((S_IWUSR & stat_buf.st_mode) ||
598 (S_IWGRP & stat_buf.st_mode) ||
599 (S_IWOTH & stat_buf.st_mode))) {
600 dir_info.attrs.attribute |= MTP_FILE_ATTR_MODE_READ_ONLY;
603 obj = _entity_add_file_to_store(store, h_parent, fullpath,
604 file_name, &dir_info);
606 ERR("_entity_add_file_to_store fail.");
609 } else if (S_ISDIR(stat_buf.st_mode)) {
610 dir_info.type = MTP_DIR_TYPE;
611 dir_info.attrs.attribute |= MTP_FILE_ATTR_MODE_DIR;
612 obj = _entity_add_folder_to_store(store, h_parent, fullpath,
613 file_name, &dir_info);
615 ERR("_entity_add_folder_to_store fail.");
619 ERR("%s type is neither DIR nor FILE.\n", fullpath);
623 _eh_send_event_req_to_eh_thread(EVENT_OBJECT_ADDED,
624 obj->obj_handle, 0, NULL);
629 static void __process_object_deleted_event(mtp_char *fullpath,
630 mtp_char *file_name, mtp_bool isdir)
632 mtp_obj_t *obj = NULL;
633 mtp_obj_t *parent_obj = NULL;
634 mtp_store_t *store = NULL;
635 mtp_uint32 storageid = 0;
636 mtp_uint32 h_parent = 0;
637 mtp_uint32 obj_handle = 0;
638 slist_node_t *node = NULL;
640 if (NULL != strstr(fullpath, MTP_TEMP_FILE)) {
641 ERR("File is a temp file, need to ignore");
645 if (file_name[0] == '.') {
646 DBG_SECURE("Hidden file filename=[%s], Ignore\n", file_name);
650 storageid = _entity_get_store_id_by_path(fullpath);
651 store = _device_get_store(storageid);
653 ERR("store is NULL so return");
657 obj = _entity_get_object_from_store_by_path(store, fullpath);
659 ERR("object is NULL so return");
663 obj_handle = obj->obj_handle;
664 h_parent = obj->obj_info->h_parent;
665 if (h_parent != PTP_OBJECTHANDLE_ROOT) {
666 parent_obj = _entity_get_object_from_store(store, h_parent);
667 if (NULL != parent_obj) {
668 _entity_remove_reference_child_array(parent_obj,
674 __delete_children_from_store_inoti(store, obj);
676 node = _util_delete_node(&(store->obj_list), obj);
678 _entity_dealloc_mtp_obj(obj);
680 _eh_send_event_req_to_eh_thread(EVENT_OBJECT_REMOVED, obj_handle,
686 static void __destroy_inoti_open_files_list()
688 open_files_info_t *current = NULL;
690 ret_if(g_open_files_list == NULL);
692 while (g_open_files_list) {
693 current = g_open_files_list;
694 g_open_files_list = g_open_files_list->previous;
696 if (g_open_files_list)
697 g_open_files_list->next = NULL;
699 g_free(current->name);
701 current->previous = NULL;
702 current->next = NULL;
706 g_open_files_list = NULL;
709 #endif /*MTP_SUPPORT_OBJECTADDDELETE_EVENT*/