1/* MiniDLNA media server 2 * Copyright (C) 2008-2010 Justin Maggard 3 * 4 * This file is part of MiniDLNA. 5 * 6 * MiniDLNA is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * MiniDLNA is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>. 17 */ 18#include "config.h" 19 20#ifdef HAVE_INOTIFY 21#include <stdio.h> 22#include <string.h> 23#include <stdlib.h> 24#include <errno.h> 25#include <unistd.h> 26#include <dirent.h> 27#include <libgen.h> 28#include <errno.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <sys/time.h> 32#include <sys/resource.h> 33#include <poll.h> 34#include <sys/inotify.h> 35/* Foxconn added start Bernie 06/01/2016 */ 36#include "../ffmpeg-2.3.4/libavutil/avutil.h" 37#include "../ffmpeg-2.3.4/libavcodec/avcodec.h" 38#include "../ffmpeg-2.3.4/libavformat/avformat.h" 39/* Foxconn added end Bernie 06/01/2016 */ 40 41#include "upnpglobalvars.h" 42#include "inotify.h" 43#include "utils.h" 44#include "sql.h" 45#include "scanner.h" 46#include "metadata.h" 47#include "albumart.h" 48#include "playlist.h" 49#include "log.h" 50 51#define EVENT_SIZE ( sizeof (struct inotify_event) ) 52#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) 53#define DESIRED_WATCH_LIMIT 65536 54 55#define PATH_BUF_SIZE PATH_MAX 56 57struct watch 58{ 59 int wd; /* watch descriptor */ 60 char *path; /* watched path */ 61 struct watch *next; 62}; 63 64static struct watch *watches; 65static struct watch *lastwatch = NULL; 66static time_t next_pl_fill = 0; 67 68char *get_path_from_wd(int wd) 69{ 70 struct watch *w = watches; 71 72 while( w != NULL ) 73 { 74 if( w->wd == wd ) 75 return w->path; 76 w = w->next; 77 } 78 79 return NULL; 80} 81 82int 83add_watch(int fd, const char * path) 84{ 85 struct watch *nw; 86 int wd; 87 88 wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE); 89 if( wd < 0 ) 90 { 91 DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch(%s) [%s]\n", path, strerror(errno)); 92 return -1; 93 } 94 95 nw = malloc(sizeof(struct watch)); 96 if( nw == NULL ) 97 { 98 DPRINTF(E_ERROR, L_INOTIFY, "malloc() error\n"); 99 return -1; 100 } 101 nw->wd = wd; 102 nw->next = NULL; 103 nw->path = strdup(path); 104 105 if( watches == NULL ) 106 { 107 watches = nw; 108 } 109 110 if( lastwatch != NULL ) 111 { 112 lastwatch->next = nw; 113 } 114 lastwatch = nw; 115 116 return wd; 117} 118 119int 120remove_watch(int fd, const char * path) 121{ 122 struct watch *w; 123 124 for( w = watches; w; w = w->next ) 125 { 126 if( strcmp(path, w->path) == 0 ) 127 return(inotify_rm_watch(fd, w->wd)); 128 } 129 130 return 1; 131} 132 133unsigned int 134next_highest(unsigned int num) 135{ 136 num |= num >> 1; 137 num |= num >> 2; 138 num |= num >> 4; 139 num |= num >> 8; 140 num |= num >> 16; 141 return(++num); 142} 143 144int 145inotify_create_watches(int fd) 146{ 147 FILE * max_watches; 148 unsigned int num_watches = 0, watch_limit; 149 char **result; 150 int i, rows = 0; 151 struct media_dir_s * media_path; 152 153 for( media_path = media_dirs; media_path != NULL; media_path = media_path->next ) 154 { 155 DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", media_path->path); 156 add_watch(fd, media_path->path); 157 num_watches++; 158 } 159 sql_get_table(db, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result, &rows, NULL); 160 for( i=1; i <= rows; i++ ) 161 { 162 DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", result[i]); 163 add_watch(fd, result[i]); 164 num_watches++; 165 } 166 sqlite3_free_table(result); 167 168 max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r"); 169 if( max_watches ) 170 { 171 if( fscanf(max_watches, "%10u", &watch_limit) < 1 ) 172 watch_limit = 8192; 173 fclose(max_watches); 174 if( (watch_limit < DESIRED_WATCH_LIMIT) || (watch_limit < (num_watches*4/3)) ) 175 { 176 max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "w"); 177 if( max_watches ) 178 { 179 if( DESIRED_WATCH_LIMIT >= (num_watches*3/4) ) 180 { 181 fprintf(max_watches, "%u", DESIRED_WATCH_LIMIT); 182 } 183 else if( next_highest(num_watches) >= (num_watches*3/4) ) 184 { 185 fprintf(max_watches, "%u", next_highest(num_watches)); 186 } 187 else 188 { 189 fprintf(max_watches, "%u", next_highest(next_highest(num_watches))); 190 } 191 fclose(max_watches); 192 } 193 else 194 { 195 DPRINTF(E_WARN, L_INOTIFY, "WARNING: Inotify max_user_watches [%u] is low or close to the number of used watches [%u] " 196 "and I do not have permission to increase this limit. Please do so manually by " 197 "writing a higher value into /proc/sys/fs/inotify/max_user_watches.\n", watch_limit, num_watches); 198 } 199 } 200 } 201 else 202 { 203 DPRINTF(E_WARN, L_INOTIFY, "WARNING: Could not read inotify max_user_watches! " 204 "Hopefully it is enough to cover %u current directories plus any new ones added.\n", num_watches); 205 } 206 207 return rows; 208} 209 210int 211inotify_remove_watches(int fd) 212{ 213 struct watch *w = watches; 214 struct watch *last_w; 215 int rm_watches = 0; 216 217 while( w ) 218 { 219 last_w = w; 220 inotify_rm_watch(fd, w->wd); 221 free(w->path); 222 rm_watches++; 223 w = w->next; 224 free(last_w); 225 } 226 227 return rm_watches; 228} 229 230int add_dir_watch(int fd, char * path, char * filename) 231{ 232 DIR *ds; 233 struct dirent *e; 234 char *dir; 235 char buf[PATH_MAX]; 236 int wd; 237 int i = 0; 238 239 if( filename ) 240 { 241 snprintf(buf, sizeof(buf), "%s/%s", path, filename); 242 dir = buf; 243 } 244 else 245 dir = path; 246 247 wd = add_watch(fd, dir); 248 if( wd == -1 ) 249 { 250 DPRINTF(E_ERROR, L_INOTIFY, "add_watch() [%s]\n", strerror(errno)); 251 } 252 else 253 { 254 DPRINTF(E_INFO, L_INOTIFY, "Added watch to %s [%d]\n", dir, wd); 255 } 256 257 ds = opendir(dir); 258 if( ds != NULL ) 259 { 260 while( (e = readdir(ds)) ) 261 { 262 if( strcmp(e->d_name, ".") == 0 || 263 strcmp(e->d_name, "..") == 0 ) 264 continue; 265 if( (e->d_type == DT_DIR) || 266 (e->d_type == DT_UNKNOWN && resolve_unknown_type(dir, NO_MEDIA) == TYPE_DIR) ) 267 i += add_dir_watch(fd, dir, e->d_name); 268 } 269 } 270 else 271 { 272 DPRINTF(E_ERROR, L_INOTIFY, "Opendir error! [%s]\n", strerror(errno)); 273 } 274 closedir(ds); 275 i++; 276 277 return(i); 278} 279 280int 281inotify_insert_file(char * name, const char * path) 282{ 283 int len; 284 char * last_dir; 285 char * path_buf; 286 char * base_name; 287 char * base_copy; 288 char * parent_buf = NULL; 289 char * id = NULL; 290 int depth = 1; 291 int ts; 292 media_types types = ALL_MEDIA; 293 struct media_dir_s * media_path = media_dirs; 294 struct stat st; 295 296 /* Is it cover art for another file? */ 297 if( is_image(path) ) 298 update_if_album_art(path); 299 else if( is_caption(path) ) 300 check_for_captions(path, 0); 301 302 /* Check if we're supposed to be scanning for this file type in this directory */ 303 while( media_path ) 304 { 305 if( strncmp(path, media_path->path, strlen(media_path->path)) == 0 ) 306 { 307 types = media_path->types; 308 break; 309 } 310 media_path = media_path->next; 311 } 312 switch( types ) 313 { 314 case ALL_MEDIA: 315 if( !is_image(path) && 316 !is_audio(path) && 317 !is_video(path) && 318 !is_playlist(path) ) 319 return -1; 320 break; 321 case TYPE_AUDIO: 322 if( !is_audio(path) && 323 !is_playlist(path) ) 324 return -1; 325 break; 326 case TYPE_AUDIO|TYPE_VIDEO: 327 if( !is_audio(path) && 328 !is_video(path) && 329 !is_playlist(path) ) 330 return -1; 331 break; 332 case TYPE_AUDIO|TYPE_IMAGES: 333 if( !is_image(path) && 334 !is_audio(path) && 335 !is_playlist(path) ) 336 return -1; 337 break; 338 case TYPE_VIDEO: 339 if( !is_video(path) ) 340 return -1; 341 break; 342 case TYPE_VIDEO|TYPE_IMAGES: 343 if( !is_image(path) && 344 !is_video(path) ) 345 return -1; 346 break; 347 case TYPE_IMAGES: 348 if( !is_image(path) ) 349 return -1; 350 break; 351 default: 352 return -1; 353 break; 354 } 355 356 /* If it's already in the database and hasn't been modified, skip it. */ 357 if( stat(path, &st) != 0 ) 358 return -1; 359 360 ts = sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path); 361 if( !ts && is_playlist(path) && (sql_get_int_field(db, "SELECT ID from PLAYLISTS where PATH = '%q'", path) > 0) ) 362 { 363 DPRINTF(E_DEBUG, L_INOTIFY, "Re-reading modified playlist (%s).\n", path); 364 inotify_remove_file(path); 365 next_pl_fill = 1; 366 } 367 else if( ts < st.st_mtime ) 368 { 369 if( ts > 0 ) 370 DPRINTF(E_DEBUG, L_INOTIFY, "%s is newer than the last db entry.\n", path); 371 inotify_remove_file(path); 372 } 373 374 /* Find the parentID. If it's not found, create all necessary parents. */ 375 len = strlen(path)+1; 376 if( !(path_buf = malloc(len)) || 377 !(last_dir = malloc(len)) || 378 !(base_name = malloc(len)) ) 379 return -1; 380 base_copy = base_name; 381 while( depth ) 382 { 383 depth = 0; 384 strcpy(path_buf, path); 385 parent_buf = dirname(path_buf); 386 387 do 388 { 389 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf); 390 id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" 391 " where d.PATH = '%q' and REF_ID is NULL", parent_buf); 392 if( id ) 393 { 394 if( !depth ) 395 break; 396 DPRINTF(E_DEBUG, L_INOTIFY, "Found first known parentID: %s [%s]\n", id, parent_buf); 397 /* Insert newly-found directory */ 398 strcpy(base_name, last_dir); 399 base_copy = basename(base_name); 400 insert_directory(base_copy, last_dir, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); 401 sqlite3_free(id); 402 break; 403 } 404 depth++; 405 strcpy(last_dir, parent_buf); 406 parent_buf = dirname(parent_buf); 407 } 408 while( strcmp(parent_buf, "/") != 0 ); 409 410 if( strcmp(parent_buf, "/") == 0 ) 411 { 412 id = sqlite3_mprintf("%s", BROWSEDIR_ID); 413 depth = 0; 414 break; 415 } 416 strcpy(path_buf, path); 417 } 418 free(last_dir); 419 free(path_buf); 420 free(base_name); 421 422 if( !depth ) 423 { 424 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Inserting %s\n", name); 425 insert_file(name, path, id+2, get_next_available_id("OBJECTS", id), types); 426 sqlite3_free(id); 427 if( (is_audio(path) || is_playlist(path)) && next_pl_fill != 1 ) 428 { 429 next_pl_fill = time(NULL) + 120; // Schedule a playlist scan for 2 minutes from now. 430 //DEBUG DPRINTF(E_WARN, L_INOTIFY, "Playlist scan scheduled for %s", ctime(&next_pl_fill)); 431 } 432 } 433 return depth; 434} 435 436int 437inotify_insert_directory(int fd, char *name, const char * path) 438{ 439 DIR * ds; 440 struct dirent * e; 441 char *id, *parent_buf, *esc_name; 442 char path_buf[PATH_MAX]; 443 int wd; 444 enum file_types type = TYPE_UNKNOWN; 445 media_types dir_types = ALL_MEDIA; 446 struct media_dir_s* media_path; 447 struct stat st; 448 449 if( access(path, R_OK|X_OK) != 0 ) 450 { 451 DPRINTF(E_WARN, L_INOTIFY, "Could not access %s [%s]\n", path, strerror(errno)); 452 return -1; 453 } 454 if( sql_get_int_field(db, "SELECT ID from DETAILS where PATH = '%q'", path) > 0 ) 455 { 456 DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path); 457 return 0; 458 } 459 460 parent_buf = strdup(path); 461 id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" 462 " where d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf)); 463 if( !id ) 464 id = sqlite3_mprintf("%s", BROWSEDIR_ID); 465 insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); 466 sqlite3_free(id); 467 free(parent_buf); 468 469 wd = add_watch(fd, path); 470 if( wd == -1 ) 471 { 472 DPRINTF(E_ERROR, L_INOTIFY, "add_watch() failed\n"); 473 } 474 else 475 { 476 DPRINTF(E_INFO, L_INOTIFY, "Added watch to %s [%d]\n", path, wd); 477 } 478 479 media_path = media_dirs; 480 while( media_path ) 481 { 482 if( strncmp(path, media_path->path, strlen(media_path->path)) == 0 ) 483 { 484 dir_types = media_path->types; 485 break; 486 } 487 media_path = media_path->next; 488 } 489 490 ds = opendir(path); 491 if( !ds ) 492 { 493 DPRINTF(E_ERROR, L_INOTIFY, "opendir failed! [%s]\n", strerror(errno)); 494 return -1; 495 } 496 while( (e = readdir(ds)) ) 497 { 498 if( e->d_name[0] == '.' ) 499 continue; 500 esc_name = escape_tag(e->d_name, 1); 501 snprintf(path_buf, sizeof(path_buf), "%s/%s", path, e->d_name); 502 switch( e->d_type ) 503 { 504 case DT_DIR: 505 case DT_REG: 506 case DT_LNK: 507 case DT_UNKNOWN: 508 type = resolve_unknown_type(path_buf, dir_types); 509 default: 510 break; 511 } 512 if( type == TYPE_DIR ) 513 { 514 inotify_insert_directory(fd, esc_name, path_buf); 515 } 516 else if( type == TYPE_FILE ) 517 { 518 if( (stat(path_buf, &st) == 0) && (st.st_blocks<<9 >= st.st_size) ) 519 { 520 inotify_insert_file(esc_name, path_buf); 521 } 522 } 523 free(esc_name); 524 } 525 closedir(ds); 526 527 return 0; 528} 529 530int 531inotify_remove_file(const char * path) 532{ 533 char sql[128]; 534 char art_cache[PATH_MAX]; 535 char *id; 536 char *ptr; 537 char **result; 538 int64_t detailID; 539 int rows, playlist; 540 541 if( is_caption(path) ) 542 { 543 return sql_exec(db, "DELETE from CAPTIONS where PATH = '%q'", path); 544 } 545 /* Invalidate the scanner cache so we don't insert files into non-existent containers */ 546 valid_cache = 0; 547 playlist = is_playlist(path); 548 id = sql_get_text_field(db, "SELECT ID from %s where PATH = '%q'", playlist?"PLAYLISTS":"DETAILS", path); 549 if( !id ) 550 return 1; 551 detailID = strtoll(id, NULL, 10); 552 sqlite3_free(id); 553 if( playlist ) 554 { 555 sql_exec(db, "DELETE from PLAYLISTS where ID = %lld", detailID); 556 sql_exec(db, "DELETE from DETAILS where ID =" 557 " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%llX')", 558 MUSIC_PLIST_ID, detailID); 559 sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s$%llX' or PARENT_ID = '%s$%llX'", 560 MUSIC_PLIST_ID, detailID, MUSIC_PLIST_ID, detailID); 561 } 562 else 563 { 564 /* Delete the parent containers if we are about to empty them. */ 565 snprintf(sql, sizeof(sql), "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld" 566 " and PARENT_ID not like '64$%%'", 567 (long long int)detailID); 568 if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) 569 { 570 int i, children; 571 for( i = 1; i <= rows; i++ ) 572 { 573 /* If it's a playlist item, adjust the item count of the playlist */ 574 if( strncmp(result[i], MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) 575 { 576 sql_exec(db, "UPDATE PLAYLISTS set FOUND = (FOUND-1) where ID = %d", 577 atoi(strrchr(result[i], '$') + 1)); 578 } 579 580 children = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result[i]); 581 if( children < 0 ) 582 continue; 583 if( children < 2 ) 584 { 585 sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]); 586 587 ptr = strrchr(result[i], '$'); 588 if( ptr ) 589 *ptr = '\0'; 590 if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result[i]) == 0 ) 591 { 592 sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]); 593 } 594 } 595 } 596 sqlite3_free_table(result); 597 } 598 /* Now delete the actual objects */ 599 sql_exec(db, "DELETE from DETAILS where ID = %lld", detailID); 600 sql_exec(db, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID); 601 } 602 snprintf(art_cache, sizeof(art_cache), "%s/art_cache%s", db_path, path); 603 remove(art_cache); 604 605 return 0; 606} 607 608int 609inotify_remove_directory(int fd, const char * path) 610{ 611 char * sql; 612 char **result; 613 int64_t detailID = 0; 614 int rows, i, ret = 1; 615 616 /* Invalidate the scanner cache so we don't insert files into non-existent containers */ 617 valid_cache = 0; 618 remove_watch(fd, path); 619 sql = sqlite3_mprintf("SELECT ID from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')" 620 " or PATH = '%q'", path, path, 0xFF, path); 621 if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) 622 { 623 if( rows ) 624 { 625 for( i=1; i <= rows; i++ ) 626 { 627 detailID = strtoll(result[i], NULL, 10); 628 sql_exec(db, "DELETE from DETAILS where ID = %lld", detailID); 629 sql_exec(db, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID); 630 } 631 ret = 0; 632 } 633 sqlite3_free_table(result); 634 } 635 sqlite3_free(sql); 636 /* Clean up any album art entries in the deleted directory */ 637 sql_exec(db, "DELETE from ALBUM_ART where (PATH > '%q/' and PATH <= '%q/%c')", path, path, 0xFF); 638 639 return ret; 640} 641 642void * 643start_inotify() 644{ 645 struct pollfd pollfds[1]; 646 int timeout = 1000; 647 char buffer[BUF_LEN]; 648 char path_buf[PATH_MAX]; 649 int length, i = 0; 650 char * esc_name = NULL; 651 struct stat st; 652 653/* Foxconn modify start, Bernie 06/01/2016 */ 654// pollfds[0].fd = inotify_init(); 655 pollfds[0].fd = inotify_init1(IN_CLOEXEC); 656/* Foxconn modify end, Bernie 06/01/2016 */ 657 pollfds[0].events = POLLIN; 658 659 if ( pollfds[0].fd < 0 ) 660 DPRINTF(E_ERROR, L_INOTIFY, "inotify_init() failed!\n"); 661 662 while( scanning ) 663 { 664 if( quitting ) 665 goto quitting; 666 sleep(1); 667 } 668 inotify_create_watches(pollfds[0].fd); 669 if (setpriority(PRIO_PROCESS, 0, 19) == -1) 670 DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce inotify thread priority\n"); 671 sqlite3_release_memory(1<<31); 672 av_register_all(); 673 674 while( !quitting ) 675 { 676 length = poll(pollfds, 1, timeout); 677 if( !length ) 678 { 679 if( next_pl_fill && (time(NULL) >= next_pl_fill) ) 680 { 681 fill_playlists(); 682 next_pl_fill = 0; 683 } 684 continue; 685 } 686 else if( length < 0 ) 687 { 688 if( (errno == EINTR) || (errno == EAGAIN) ) 689 continue; 690 else 691 DPRINTF(E_ERROR, L_INOTIFY, "read failed!\n"); 692 } 693 else 694 { 695 length = read(pollfds[0].fd, buffer, BUF_LEN); 696 buffer[BUF_LEN-1] = '\0'; 697 } 698 699 i = 0; 700 while( i < length ) 701 { 702 struct inotify_event * event = (struct inotify_event *) &buffer[i]; 703 if( event->len ) 704 { 705 if( *(event->name) == '.' ) 706 { 707 i += EVENT_SIZE + event->len; 708 continue; 709 } 710 esc_name = modifyString(strdup(event->name), "&", "&amp;", 0); 711 snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name); 712 if ( event->mask & IN_ISDIR && (event->mask & (IN_CREATE|IN_MOVED_TO)) ) 713 { 714 DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n", 715 path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); 716 inotify_insert_directory(pollfds[0].fd, esc_name, path_buf); 717 } 718 else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) && 719 (lstat(path_buf, &st) == 0) ) 720 { 721/* Foxconn modify start, Bernie 06/06/2016 @ The original code can not establish a hidden directory.*/ 722// if( (event->mask & (IN_MOVED_TO|IN_CREATE)) && (S_ISLNK(st.st_mode) || st.st_nlink > 1) ) 723// { 724// DPRINTF(E_DEBUG, L_INOTIFY, "The %s link %s was %s.\n", 725// (S_ISLNK(st.st_mode) ? "symbolic" : "hard"), 726 if( S_ISLNK(st.st_mode) ) 727 { 728 DPRINTF(E_DEBUG, L_INOTIFY, "The symbolic link %s was %s.\n", 729/* Foxconn modify start, Bernie 06/06/2016 */ 730 path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); 731 if( stat(path_buf, &st) == 0 && S_ISDIR(st.st_mode) ) 732 inotify_insert_directory(pollfds[0].fd, esc_name, path_buf); 733 else 734 inotify_insert_file(esc_name, path_buf); 735 } 736 else if( event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO) && st.st_size > 0 ) 737 { 738 if( (event->mask & IN_MOVED_TO) || 739 (sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path_buf) != st.st_mtime) ) 740 { 741 DPRINTF(E_DEBUG, L_INOTIFY, "The file %s was %s.\n", 742 path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "changed")); 743 inotify_insert_file(esc_name, path_buf); 744 } 745 } 746 } 747 else if ( event->mask & (IN_DELETE|IN_MOVED_FROM) ) 748 { 749 DPRINTF(E_DEBUG, L_INOTIFY, "The %s %s was %s.\n", 750 (event->mask & IN_ISDIR ? "directory" : "file"), 751 path_buf, (event->mask & IN_MOVED_FROM ? "moved away" : "deleted")); 752 if ( event->mask & IN_ISDIR ) 753 inotify_remove_directory(pollfds[0].fd, path_buf); 754 else 755 inotify_remove_file(path_buf); 756 } 757 free(esc_name); 758 } 759 i += EVENT_SIZE + event->len; 760 } 761 } 762 inotify_remove_watches(pollfds[0].fd); 763quitting: 764 close(pollfds[0].fd); 765 766 return 0; 767} 768#endif 769