Revert "Revert "improve plugin architecture""
[sdk/target/sdbd.git] / src / file_sync_service.c
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <dirent.h>
24 #include <utime.h>
25 #include <regex.h>
26 #include <errno.h>
27 #include <sys/socket.h>
28 #include <sys/select.h>
29 #include "sysdeps.h"
30 #include <sys/smack.h>
31 #include <tzplatform_config.h>
32
33 #define TRACE_TAG  TRACE_SYNC
34 #include "log.h"
35
36 #include "sdb.h"
37 #include "file_sync_service.h"
38 #include "sdktools.h"
39 #include "sdbd_plugin.h"
40 #include "utils.h"
41
42 #define SYNC_TIMEOUT 15
43
44 struct sync_permit_rule
45 {
46     const char *name;
47     char *regx;
48     int mode; // 0:push, 1: pull, 2: push&push
49 };
50
51 struct sync_permit_rule sdk_sync_permit_rule[] = {
52     /* 0 */ {"unitest", "", 1},
53     /* 1 */ {"codecoverage", "", 1},
54     /* 2 */ {"da", "", 1},
55     /* end */ {NULL, NULL, 0}
56 };
57
58 /* The typical default value for the umask is S_IWGRP | S_IWOTH (octal 022).
59  * Before use the DIR_PERMISSION, the process umask value should be set 0 using umask().
60  */
61 #define DIR_PERMISSION 0777
62
63 void init_sdk_sync_permit_rule_regx(void)
64 {
65     int ret;
66     ret = asprintf(&sdk_sync_permit_rule[0].regx, "^((/tmp)|(%s)|(%s))/[a-zA-Z0-9]{10}/data/[a-zA-Z0-9_\\-]{1,50}\\.xml$", APP_INSTALL_PATH_PREFIX1, APP_INSTALL_PATH_PREFIX2);
67     if(ret < 0) {
68         D("failed to run asprintf for unittest\n");
69     }
70     ret = asprintf(&sdk_sync_permit_rule[1].regx, "^((/tmp)|(%s)|(%s))/[a-zA-Z0-9]{10}/data/+(.)*\\.gcda$", APP_INSTALL_PATH_PREFIX1, APP_INSTALL_PATH_PREFIX2);
71     if (ret < 0) {
72         D("failed to run asprintf for codecoverage\n");
73     }
74     ret = asprintf(&sdk_sync_permit_rule[2].regx, "^(/tmp/da/)*+[a-zA-Z0-9_\\-\\.]{1,50}\\.png$");
75     if (ret < 0) {
76         D("failed to run asprintf for da\n");
77     }
78 }
79
80 static void set_syncfile_smack_label(char *src) {
81     char *label_transmuted = NULL;
82     char *label = NULL;
83     char *src_chr = strrchr(src, '/');
84     int pos = src_chr - src + 1;
85     char dirname[512];
86
87     if (getuid() != 0) {
88         D("need root permission to set smack label: %d\n", getuid());
89         return;
90     }
91
92     snprintf(dirname, pos, "%s", src);
93
94     //D("src:[%s], dirname:[%s]\n", src, dirname);
95     int rc = smack_getlabel(dirname, &label_transmuted, SMACK_LABEL_TRANSMUTE);
96
97     if (rc == 0 && label_transmuted != NULL) {
98         if (!strcmp("TRUE", label_transmuted)) {
99             rc = smack_getlabel(dirname, &label, SMACK_LABEL_ACCESS);
100             if (rc == 0 && label != NULL) {
101                 if (smack_setlabel(src, label, SMACK_LABEL_ACCESS) == -1) {
102                     D("unable to set sync file smack label %s due to (errno:%d)\n", label, errno);
103                 }
104
105                 /* Todo: The following code is from tizen 2.4
106                 rc = security_server_label_access(src, label);
107                 if (rc != SECURITY_SERVER_API_SUCCESS) {
108                    D("unable to set sync file smack label %s due to %d\n", label, errno);
109                 }
110                 */
111                 free(label);
112             }
113         } else{
114             D("fail to set label, is it transmuted?:%s\n", label_transmuted);
115         }
116         free(label_transmuted);
117     } else {
118         if (smack_setlabel(src, SMACK_SYNC_FILE_LABEL, SMACK_LABEL_ACCESS) == -1) {
119             D("unable to set sync file smack label %s due to (errno:%d)\n", SMACK_SYNC_FILE_LABEL, errno);
120         }
121
122         /* Todo: The following code is from tizen 2.4
123         rc = security_server_label_access(src, SMACK_SYNC_FILE_LABEL);
124         if (rc != SECURITY_SERVER_API_SUCCESS) {
125            D("unable to set sync file smack label %s due to %d\n", SMACK_SYNC_FILE_LABEL, errno);
126         }
127         */
128     }
129 }
130
131 static int sync_send_label_notify(int s, const char *path, int success)
132 {
133     char buffer[512] = {0,};
134     snprintf(buffer, sizeof(buffer), "%d:%s", success, path);
135
136     int len = sdb_write(s, buffer, sizeof(buffer));
137
138     return len;
139 }
140
141 static void sync_read_label_notify(int s)
142 {
143     char buffer[512 + 1] = {0,};
144
145     while (1) {
146         int len = sdb_read(s, buffer, sizeof(buffer) - 1);
147         if (len < 0) {
148             D("sync notify read errno:%d\n", errno);
149             exit(-1);
150         }
151
152         if (buffer[0] == '0') {
153             D("sync notify child process exit\n");
154             exit(-1);
155         }
156         buffer[len] = '\0';
157         char *path = buffer;
158         path++;
159         path++;
160         set_syncfile_smack_label(path);
161     }
162 }
163
164 static int mkdirs(int noti_fd, char *name)
165 {
166     int ret;
167     char *x = name + 1;
168
169     if(name[0] != '/') {
170         return -1;
171     }
172
173     for(;;) {
174         x = sdb_dirstart(x);
175         if(x == 0) {
176             return 0;
177         }
178         *x = 0;
179
180         ret = sdb_mkdir(name, DIR_PERMISSION);
181         if (ret == 0) {
182             sync_send_label_notify(noti_fd, name, 1);
183         }
184         if((ret < 0) && (errno != EEXIST)) {
185             D("mkdir(\"%s\") -> errno:%d\n", name, errno);
186             *x = '/';
187             return ret;
188         }
189         *x++ = '/';
190     }
191     return 0;
192 }
193
194 static int do_stat(int s, const char *path)
195 {
196     syncmsg msg;
197     struct stat st;
198
199     msg.stat.id = ID_STAT;
200
201     /* follow link */
202     if(stat(path, &st)) {
203         msg.stat.mode = 0;
204         msg.stat.size = 0;
205         msg.stat.time = 0;
206         D("failed to stat %s due to: errno:%d\n", path, errno);
207     } else {
208         msg.stat.mode = htoll(st.st_mode);
209         msg.stat.size = htoll(st.st_size);
210         msg.stat.time = htoll(st.st_mtime);
211     }
212
213     return writex(s, &msg.stat, sizeof(msg.stat));
214 }
215
216 static int do_list(int s, const char *path)
217 {
218     DIR *d;
219     struct dirent *de;
220     struct stat st;
221     syncmsg msg;
222     int len;
223
224     char tmp[1024 + 256 + 1];
225     char *fname;
226
227     char dirent_buffer[ sizeof(struct dirent) + 260 + 1 ]  = {0,};
228     struct dirent *dirent_r = (struct dirent*)dirent_buffer;
229
230     len = strlen(path);
231     memcpy(tmp, path, len);
232     tmp[len] = '/';
233     fname = tmp + len + 1;
234
235     msg.dent.id = ID_DENT;
236
237     d = opendir(path);
238     if(d == NULL) {
239         D("failed to open dir due to: errno:%d\n", errno);
240         goto done;
241     }
242
243     while((readdir_r(d, dirent_r, &de) == 0) && de) {
244         int len = strlen(de->d_name);
245
246             /* not supposed to be possible, but
247                if it does happen, let's not buffer overrun */
248         if(len > 256) {
249             continue;
250         }
251
252         s_strncpy(fname, de->d_name, sizeof tmp);
253         if(lstat(tmp, &st) == 0) {
254             msg.dent.mode = htoll(st.st_mode);
255             msg.dent.size = htoll(st.st_size);
256             msg.dent.time = htoll(st.st_mtime);
257             msg.dent.namelen = htoll(len);
258
259             if(writex(s, &msg.dent, sizeof(msg.dent)) ||
260                writex(s, de->d_name, len)) {
261                 closedir(d);
262                 return -1;
263             }
264         }
265     }
266
267     closedir(d);
268
269 done:
270     msg.dent.id = ID_DONE;
271     msg.dent.mode = 0;
272     msg.dent.size = 0;
273     msg.dent.time = 0;
274     msg.dent.namelen = 0;
275     return writex(s, &msg.dent, sizeof(msg.dent));
276 }
277
278 static int fail_message(int s, const char *reason)
279 {
280     syncmsg msg;
281     size_t len = strlen(reason);
282
283     D("sync: failure: %s\n", reason);
284
285     msg.data.id = ID_FAIL;
286     msg.data.size = htoll(len);
287
288     if(writex(s, &msg.data, sizeof(msg.data)) ||
289        writex(s, reason, len)) {
290         return -1;
291     } else {
292         return 0;
293     }
294 }
295
296 static int fail_errno(int fd, int err_no)
297 {
298     char* ret_str;
299     char buf[512] = {0, };
300
301     ret_str = strerror_r(err_no, buf, sizeof(buf));
302
303     return fail_message(fd, (const char*)ret_str);
304 }
305
306 // FIXME: should get the following paths with api later but, do it for simple and not having dependency on other packages
307 #define VAR_ABS_PATH         "/opt/var"
308 #define CMD_MEDIADB_UPDATE   tzplatform_mkpath(TZ_SYS_BIN, "mediadb-update")
309 #define MEDIA_CONTENTS_PATH1 tzplatform_getenv(TZ_SYS_STORAGE)
310 #define MEDIA_CONTENTS_PATH2 tzplatform_getenv(TZ_USER_CONTENT)
311 #define MEDIA_CONTENTS_PATH3 tzplatform_mkpath(TZ_SYS_STORAGE, "sdcard")
312
313 static void sync_mediadb(char *path) {
314     if (access(CMD_MEDIADB_UPDATE, F_OK) != 0) {
315         D("%s: command not found\n", CMD_MEDIADB_UPDATE);
316         return;
317     }
318
319     if (strstr(path, VAR_ABS_PATH) == path) {
320         path += 4;
321     }
322
323     if (strstr(path, MEDIA_CONTENTS_PATH1) != NULL) {
324         const char * const arg_list[] = {CMD_MEDIADB_UPDATE, "-r", MEDIA_CONTENTS_PATH1, NULL};
325         spawn(CMD_MEDIADB_UPDATE, (char * const*)arg_list);
326         D("media db update done to %s\n", MEDIA_CONTENTS_PATH1);
327     } else if (strstr(path, MEDIA_CONTENTS_PATH2) != NULL) {
328         const char * const arg_list[] = {CMD_MEDIADB_UPDATE, "-r", MEDIA_CONTENTS_PATH2, NULL};
329         spawn(CMD_MEDIADB_UPDATE, (char * const*)arg_list);
330         D("media db update done to %s\n", MEDIA_CONTENTS_PATH2);
331     } else if (strstr(path, MEDIA_CONTENTS_PATH3) != NULL) {
332         const char * const arg_list[] = {CMD_MEDIADB_UPDATE, "-r", MEDIA_CONTENTS_PATH3, NULL};
333         spawn(CMD_MEDIADB_UPDATE, (char * const*)arg_list);
334         D("media db update done to %s\n", MEDIA_CONTENTS_PATH3);
335     }
336     return;
337 }
338
339 static int handle_send_file(int s, int noti_fd, char *path, mode_t mode, char *buffer)
340 {
341     syncmsg msg;
342     unsigned int timestamp = 0;
343     int fd;
344
345     fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
346     if(fd < 0 && errno == ENOENT) {
347         mkdirs(noti_fd, path);
348         fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
349     }
350     if(fd < 0 && errno == EEXIST) {
351         fd = sdb_open_mode(path, O_WRONLY, mode);
352     }
353     if(fd < 0) {
354         if(fail_errno(s, errno))
355             return -1;
356         fd = -1;
357     }
358     for(;;) {
359         unsigned int len;
360
361         if(readx(s, &msg.data, sizeof(msg.data))) {
362             goto fail;
363         }
364         if(msg.data.id != ID_DATA) {
365             if(msg.data.id == ID_DONE) {
366                 timestamp = ltohl(msg.data.size);
367                 break;
368             }
369             fail_message(s, "invalid data message");
370             goto fail;
371         }
372         len = ltohl(msg.data.size);
373         if(len > SYNC_DATA_MAX) {
374             fail_message(s, "oversize data message");
375             goto fail;
376         }
377         if(readx(s, buffer, len)) {
378             D("read failed due to unknown reason\n");
379             goto fail;
380         }
381
382         if(fd < 0) {
383             continue;
384         }
385         if(writex(fd, buffer, len)) {
386             int saved_errno = errno;
387             sdb_close(fd);
388             sdb_unlink(path);
389             fd = -1;
390             errno = saved_errno;
391             if(fail_errno(s, errno)) return -1;
392         }
393     }
394
395     if(fd >= 0) {
396         struct utimbuf u;
397         sdb_close(fd);
398         u.actime = timestamp;
399         u.modtime = timestamp;
400         utime(path, &u);
401
402         msg.status.id = ID_OKAY;
403         msg.status.msglen = 0;
404         if(writex(s, &msg.status, sizeof(msg.status)))
405             return -1;
406         // flush file system buffers due to N_SE-22305
407         sync();
408     } else {
409         D("sync error: %d!!!\n", fd);
410         return -1;
411     }
412     sync_send_label_notify(noti_fd, path, 1);
413     sync_mediadb(path);
414     return 0;
415
416 fail:
417     if(fd >= 0)
418         sdb_close(fd);
419     sdb_unlink(path);
420     return -1;
421 }
422
423 #ifdef HAVE_SYMLINKS
424 static int handle_send_link(int s, int noti_fd, char *path, char *buffer)
425 {
426     syncmsg msg;
427     unsigned int len;
428     int ret;
429
430     if(readx(s, &msg.data, sizeof(msg.data)))
431         return -1;
432
433     if(msg.data.id != ID_DATA) {
434         fail_message(s, "invalid data message: expected ID_DATA");
435         return -1;
436     }
437
438     len = ltohl(msg.data.size);
439     if(len > SYNC_DATA_MAX) {
440         fail_message(s, "oversize data message");
441         return -1;
442     }
443     if(readx(s, buffer, len))
444         return -1;
445
446     ret = symlink(buffer, path);
447     if(ret && errno == ENOENT) {
448         mkdirs(noti_fd, path);
449         ret = symlink(buffer, path);
450     }
451     if(ret) {
452         fail_errno(s, errno);
453         return -1;
454     }
455
456     if(readx(s, &msg.data, sizeof(msg.data)))
457         return -1;
458
459     if(msg.data.id == ID_DONE) {
460         msg.status.id = ID_OKAY;
461         msg.status.msglen = 0;
462         if(writex(s, &msg.status, sizeof(msg.status)))
463             return -1;
464     } else {
465         fail_message(s, "invalid data message: expected ID_DONE");
466         return -1;
467     }
468
469     return 0;
470 }
471 #endif /* HAVE_SYMLINKS */
472
473 static int is_support_push()
474 {
475     return (!strncmp(g_capabilities.filesync_support, PLUGIN_RET_PUSHPULL, strlen(PLUGIN_RET_PUSHPULL))
476             || !strncmp(g_capabilities.filesync_support, PLUGIN_RET_PUSH, strlen(PLUGIN_RET_PUSH)));
477 }
478
479 static int is_support_pull()
480 {
481     return (!strncmp(g_capabilities.filesync_support, PLUGIN_RET_PUSHPULL, strlen(PLUGIN_RET_PUSHPULL))
482             || !strncmp(g_capabilities.filesync_support, PLUGIN_RET_PULL, strlen(PLUGIN_RET_PULL)));
483 }
484
485 static int do_send(int s, int noti_fd, char *path, char *buffer)
486 {
487     char *tmp;
488     mode_t mode;
489     int ret;
490 #ifdef HAVE_SYMLINKS
491     int is_link;
492 #endif
493
494     // Check the capability for file push support.
495     if(!is_support_push()) {
496         fail_message(s, "NO support file push.");
497         return -1;
498     }
499
500     tmp = strrchr(path,',');
501     if(tmp) {
502         *tmp = 0;
503         errno = 0;
504         mode = strtoul(tmp + 1, NULL, 0);
505 #ifdef HAVE_SYMLINKS
506         is_link = S_ISLNK(mode);
507 #endif
508         // extracts file permission from stat.mode. (ex 100644 & 0777 = 644);
509         mode &= 0777; // combination of (S_IRWXU | S_IRWXG | S_IRWXO)
510         mode |= S_IWOTH; // SDK requirement from N_SE-43337
511     }
512     if(!tmp || errno) {
513         mode = 0644; // set default permission value in most of unix system.
514 #ifdef HAVE_SYMLINKS
515         is_link = 0;
516 #endif
517     }
518     if (is_pkg_file_path(path)) {
519         mode = 0644;
520 #ifdef HAVE_SYMLINKS
521         is_link = 0;
522 #endif
523     }
524
525     // sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
526     sdb_unlink(path);
527
528
529 #ifdef HAVE_SYMLINKS
530     if(is_link)
531         ret = handle_send_link(s, noti_fd, path, buffer);
532     else {
533 #else
534     {
535 #endif
536         /* copy user permission bits to "group" and "other" permissions.
537          * ex) 0644 file will be created copied 0666 file.
538          * the following 2 lines should be commented if sdb process has been set to umask 0.
539          */
540
541         //mode |= ((mode >> 3) & 0070);
542         //mode |= ((mode >> 3) & 0007);
543         ret = handle_send_file(s, noti_fd, path, mode, buffer);
544     }
545
546     return ret;
547 }
548
549 static int do_recv(int s, const char *path, char *buffer)
550 {
551     syncmsg msg;
552     int fd, r;
553
554     // Check the capability for file push support.
555     if (!is_support_pull()) {
556         fail_message(s, "NO support file pull.");
557         return -1;
558     }
559
560     fd = sdb_open(path, O_RDONLY);
561     if(fd < 0) {
562         if(fail_errno(s, errno)) return -1;
563         return 0;
564     }
565
566     msg.data.id = ID_DATA;
567     for(;;) {
568         r = sdb_read(fd, buffer, SYNC_DATA_MAX);
569         if(r <= 0) {
570             if(r == 0) break;
571             if(errno == EINTR) continue;
572             r = fail_errno(s, errno);
573             sdb_close(fd);
574             return r;
575         }
576         msg.data.size = htoll(r);
577         if(writex(s, &msg.data, sizeof(msg.data)) ||
578            writex(s, buffer, r)) {
579             sdb_close(fd);
580             return -1;
581         }
582     }
583
584     sdb_close(fd);
585
586     msg.data.id = ID_DONE;
587     msg.data.size = 0;
588     if(writex(s, &msg.data, sizeof(msg.data))) {
589         return -1;
590     }
591     return 0;
592 }
593
594 static int verify_sync_rule(const char* path) {
595     regex_t regex;
596     int ret;
597     char buf[PATH_MAX];
598     int i=0;
599
600     init_sdk_sync_permit_rule_regx();
601     for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++) {
602         ret = regcomp(&regex, sdk_sync_permit_rule[i].regx, REG_EXTENDED);
603         if(ret){
604             return 0;
605         }
606         // execute regular expression
607         ret = regexec(&regex, path, 0, NULL, 0);
608         if(!ret){
609             regfree(&regex);
610             D("found matched rule(%s) from %s path\n", sdk_sync_permit_rule[i].name, path);
611             return 1;
612         } else if( ret == REG_NOMATCH ){
613             // do nothin
614         } else{
615             regerror(ret, &regex, buf, sizeof(buf));
616             D("regex match failed(%s): %s\n",sdk_sync_permit_rule[i].name, buf);
617         }
618     }
619     regfree(&regex);
620     for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++){
621        free(sdk_sync_permit_rule[i].regx);
622     }
623     return 0;
624 }
625
626 void file_sync_service(int fd, void *cookie)
627 {
628     syncmsg msg;
629     char name[1025];
630     unsigned namelen;
631     fd_set set;
632     struct timeval timeout;
633     int rv;
634     int s[2];
635
636     if(sdb_socketpair(s)) {
637         D("cannot create service socket pair\n");
638         exit(-1);
639     }
640     char *buffer = malloc(SYNC_DATA_MAX);
641     if(buffer == 0) {
642         goto fail;
643     }
644
645     FD_ZERO(&set); /* clear the set */
646     FD_SET(fd, &set); /* add our file descriptor to the set */
647
648     timeout.tv_sec = SYNC_TIMEOUT;
649     timeout.tv_usec = 0;
650
651     pid_t pid = fork();
652
653     if (pid == 0) {
654         sdb_close(s[0]); //close the parent fd
655         sync_read_label_notify(s[1]);
656     } else if (pid > 0) {
657         sdb_close(s[1]);
658         for(;;) {
659             D("sync: waiting for command for %d sec\n", SYNC_TIMEOUT);
660
661             rv = select(fd + 1, &set, NULL, NULL, &timeout);
662             if (rv == -1) {
663                 D("sync file descriptor select failed\n");
664             } else if (rv == 0) {
665                 D("sync file descriptor timeout: (took %d sec over)\n", SYNC_TIMEOUT);
666                 fail_message(fd, "sync timeout");
667                 goto fail;
668             }
669
670             if(readx(fd, &msg.req, sizeof(msg.req))) {
671                 fail_message(fd, "command read failure");
672                 break;
673             }
674             namelen = ltohl(msg.req.namelen);
675             if(namelen > 1024) {
676                 fail_message(fd, "invalid namelen");
677                 break;
678             }
679             if(readx(fd, name, namelen)) {
680                 fail_message(fd, "filename read failure");
681                 break;
682             }
683             name[namelen] = 0;
684
685             msg.req.namelen = 0;
686
687             D("sync: '%s' '%s'\n", (char*) &msg.req, name);
688
689             if (should_drop_privileges() && !verify_sync_rule(name)) {
690                 if (getuid() != g_sdk_user_id && set_sdk_user_privileges() < 0) {
691                     fail_message(fd, "failed to set SDK user privileges.");
692                     goto fail;
693                 }
694             }
695
696             switch(msg.req.id) {
697             case ID_STAT:
698                 if(do_stat(fd, name)) goto fail;
699                 break;
700             case ID_LIST:
701                 if(do_list(fd, name)) goto fail;
702                 break;
703             case ID_SEND:
704                 if(do_send(fd, s[0], name, buffer)) goto fail;
705                 break;
706             case ID_RECV:
707                 if(do_recv(fd, name, buffer)) goto fail;
708                 break;
709             case ID_QUIT:
710                 goto fail;
711             default:
712                 fail_message(fd, "unknown command");
713                 goto fail;
714             }
715         }
716     }
717
718
719 fail:
720     if(buffer != 0) {
721         free(buffer);
722     }
723     D("sync: done\n");
724     sync_send_label_notify(s[0], name, 0);
725     sdb_close(s[0]);
726     sdb_close(s[1]);
727     sdb_close(fd);
728 }