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