2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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.
22 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/select.h>
30 #include <sys/smack.h>
31 #include <tzplatform_config.h>
33 #define TRACE_TAG TRACE_SYNC
37 #include "file_sync_service.h"
39 #include "sdbd_plugin.h"
42 #define SYNC_TIMEOUT 15
44 struct sync_permit_rule
48 int mode; // 0:push, 1: pull, 2: push&push
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}
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().
61 #define DIR_PERMISSION 0777
63 void init_sdk_sync_permit_rule_regx(void)
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);
68 D("failed to run asprintf for unittest\n");
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);
72 D("failed to run asprintf for codecoverage\n");
74 ret = asprintf(&sdk_sync_permit_rule[2].regx, "^(/tmp/da/)*+[a-zA-Z0-9_\\-\\.]{1,50}\\.png$");
76 D("failed to run asprintf for da\n");
80 static void set_syncfile_smack_label(char *src) {
81 char *label_transmuted = NULL;
83 char *src_chr = strrchr(src, '/');
84 int pos = src_chr - src + 1;
88 D("need root permission to set smack label: %d\n", getuid());
92 snprintf(dirname, pos, "%s", src);
94 //D("src:[%s], dirname:[%s]\n", src, dirname);
95 int rc = smack_getlabel(dirname, &label_transmuted, SMACK_LABEL_TRANSMUTE);
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);
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);
114 D("fail to set label, is it transmuted?:%s\n", label_transmuted);
116 free(label_transmuted);
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);
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);
131 static int sync_send_label_notify(int s, const char *path, int success)
133 char buffer[512] = {0,};
134 snprintf(buffer, sizeof(buffer), "%d:%s", success, path);
136 int len = sdb_write(s, buffer, sizeof(buffer));
141 static void sync_read_label_notify(int s)
143 char buffer[512 + 1] = {0,};
146 int len = sdb_read(s, buffer, sizeof(buffer) - 1);
148 D("sync notify read errno:%d\n", errno);
152 if (buffer[0] == '0') {
153 D("sync notify child process exit\n");
160 set_syncfile_smack_label(path);
164 static int mkdirs(int noti_fd, char *name)
180 ret = sdb_mkdir(name, DIR_PERMISSION);
182 sync_send_label_notify(noti_fd, name, 1);
184 if((ret < 0) && (errno != EEXIST)) {
185 D("mkdir(\"%s\") -> errno:%d\n", name, errno);
194 static int do_stat(int s, const char *path)
199 msg.stat.id = ID_STAT;
202 if(stat(path, &st)) {
206 D("failed to stat %s due to: errno:%d\n", path, errno);
208 msg.stat.mode = htoll(st.st_mode);
209 msg.stat.size = htoll(st.st_size);
210 msg.stat.time = htoll(st.st_mtime);
213 return writex(s, &msg.stat, sizeof(msg.stat));
216 static int do_list(int s, const char *path)
224 char tmp[1024 + 256 + 1];
227 char dirent_buffer[ sizeof(struct dirent) + 260 + 1 ] = {0,};
228 struct dirent *dirent_r = (struct dirent*)dirent_buffer;
231 memcpy(tmp, path, len);
233 fname = tmp + len + 1;
235 msg.dent.id = ID_DENT;
239 D("failed to open dir due to: errno:%d\n", errno);
243 while((readdir_r(d, dirent_r, &de) == 0) && de) {
244 int len = strlen(de->d_name);
246 /* not supposed to be possible, but
247 if it does happen, let's not buffer overrun */
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);
259 if(writex(s, &msg.dent, sizeof(msg.dent)) ||
260 writex(s, de->d_name, len)) {
270 msg.dent.id = ID_DONE;
274 msg.dent.namelen = 0;
275 return writex(s, &msg.dent, sizeof(msg.dent));
278 static int fail_message(int s, const char *reason)
281 size_t len = strlen(reason);
283 D("sync: failure: %s\n", reason);
285 msg.data.id = ID_FAIL;
286 msg.data.size = htoll(len);
288 if(writex(s, &msg.data, sizeof(msg.data)) ||
289 writex(s, reason, len)) {
296 static int fail_errno(int fd, int err_no)
299 char buf[512] = {0, };
301 ret_str = strerror_r(err_no, buf, sizeof(buf));
303 return fail_message(fd, (const char*)ret_str);
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")
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);
319 if (strstr(path, VAR_ABS_PATH) == path) {
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);
339 static int handle_send_file(int s, int noti_fd, char *path, mode_t mode, char *buffer)
342 unsigned int timestamp = 0;
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);
350 if(fd < 0 && errno == EEXIST) {
351 fd = sdb_open_mode(path, O_WRONLY, mode);
354 if(fail_errno(s, errno))
361 if(readx(s, &msg.data, sizeof(msg.data))) {
364 if(msg.data.id != ID_DATA) {
365 if(msg.data.id == ID_DONE) {
366 timestamp = ltohl(msg.data.size);
369 fail_message(s, "invalid data message");
372 len = ltohl(msg.data.size);
373 if(len > SYNC_DATA_MAX) {
374 fail_message(s, "oversize data message");
377 if(readx(s, buffer, len)) {
378 D("read failed due to unknown reason\n");
385 if(writex(fd, buffer, len)) {
386 int saved_errno = errno;
391 if(fail_errno(s, errno)) return -1;
398 u.actime = timestamp;
399 u.modtime = timestamp;
402 msg.status.id = ID_OKAY;
403 msg.status.msglen = 0;
404 if(writex(s, &msg.status, sizeof(msg.status)))
406 // flush file system buffers due to N_SE-22305
409 D("sync error: %d!!!\n", fd);
412 sync_send_label_notify(noti_fd, path, 1);
424 static int handle_send_link(int s, int noti_fd, char *path, char *buffer)
430 if(readx(s, &msg.data, sizeof(msg.data)))
433 if(msg.data.id != ID_DATA) {
434 fail_message(s, "invalid data message: expected ID_DATA");
438 len = ltohl(msg.data.size);
439 if(len > SYNC_DATA_MAX) {
440 fail_message(s, "oversize data message");
443 if(readx(s, buffer, len))
446 ret = symlink(buffer, path);
447 if(ret && errno == ENOENT) {
448 mkdirs(noti_fd, path);
449 ret = symlink(buffer, path);
452 fail_errno(s, errno);
456 if(readx(s, &msg.data, sizeof(msg.data)))
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)))
465 fail_message(s, "invalid data message: expected ID_DONE");
471 #endif /* HAVE_SYMLINKS */
473 static int is_support_push()
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)));
479 static int is_support_pull()
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)));
485 static int do_send(int s, int noti_fd, char *path, char *buffer)
494 // Check the capability for file push support.
495 if(!is_support_push()) {
496 fail_message(s, "NO support file push.");
500 tmp = strrchr(path,',');
504 mode = strtoul(tmp + 1, NULL, 0);
506 is_link = S_ISLNK(mode);
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
513 mode = 0644; // set default permission value in most of unix system.
518 if (is_pkg_file_path(path)) {
525 // sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
531 ret = handle_send_link(s, noti_fd, path, buffer);
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.
541 //mode |= ((mode >> 3) & 0070);
542 //mode |= ((mode >> 3) & 0007);
543 ret = handle_send_file(s, noti_fd, path, mode, buffer);
549 static int do_recv(int s, const char *path, char *buffer)
554 // Check the capability for file push support.
555 if (!is_support_pull()) {
556 fail_message(s, "NO support file pull.");
560 fd = sdb_open(path, O_RDONLY);
562 if(fail_errno(s, errno)) return -1;
566 msg.data.id = ID_DATA;
568 r = sdb_read(fd, buffer, SYNC_DATA_MAX);
571 if(errno == EINTR) continue;
572 r = fail_errno(s, errno);
576 msg.data.size = htoll(r);
577 if(writex(s, &msg.data, sizeof(msg.data)) ||
578 writex(s, buffer, r)) {
586 msg.data.id = ID_DONE;
588 if(writex(s, &msg.data, sizeof(msg.data))) {
594 static int verify_sync_rule(const char* path) {
600 init_sdk_sync_permit_rule_regx();
601 for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++) {
602 ret = regcomp(®ex, sdk_sync_permit_rule[i].regx, REG_EXTENDED);
606 // execute regular expression
607 ret = regexec(®ex, path, 0, NULL, 0);
610 D("found matched rule(%s) from %s path\n", sdk_sync_permit_rule[i].name, path);
612 } else if( ret == REG_NOMATCH ){
615 regerror(ret, ®ex, buf, sizeof(buf));
616 D("regex match failed(%s): %s\n",sdk_sync_permit_rule[i].name, buf);
620 for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++){
621 free(sdk_sync_permit_rule[i].regx);
626 void file_sync_service(int fd, void *cookie)
632 struct timeval timeout;
636 if(sdb_socketpair(s)) {
637 D("cannot create service socket pair\n");
640 char *buffer = malloc(SYNC_DATA_MAX);
645 FD_ZERO(&set); /* clear the set */
646 FD_SET(fd, &set); /* add our file descriptor to the set */
648 timeout.tv_sec = SYNC_TIMEOUT;
654 sdb_close(s[0]); //close the parent fd
655 sync_read_label_notify(s[1]);
656 } else if (pid > 0) {
659 D("sync: waiting for command for %d sec\n", SYNC_TIMEOUT);
661 rv = select(fd + 1, &set, NULL, NULL, &timeout);
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");
670 if(readx(fd, &msg.req, sizeof(msg.req))) {
671 fail_message(fd, "command read failure");
674 namelen = ltohl(msg.req.namelen);
676 fail_message(fd, "invalid namelen");
679 if(readx(fd, name, namelen)) {
680 fail_message(fd, "filename read failure");
687 D("sync: '%s' '%s'\n", (char*) &msg.req, name);
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.");
698 if(do_stat(fd, name)) goto fail;
701 if(do_list(fd, name)) goto fail;
704 if(do_send(fd, s[0], name, buffer)) goto fail;
707 if(do_recv(fd, name, buffer)) goto fail;
712 fail_message(fd, "unknown command");
724 sync_send_label_notify(s[0], name, 0);