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