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
34 #define LOG_TAG "SDBD_TRACE_SYNC"
38 #include "file_sync_service.h"
40 #include "sdbd_plugin.h"
44 #define SYNC_TIMEOUT 15
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().
49 #define DIR_PERMISSION 0777
52 static void set_syncfile_smack_label(char *src) {
53 char *label_transmuted = NULL;
55 char *src_chr = strrchr(src, '/');
56 int pos = src_chr - src + 1;
60 D("need root permission to set smack label: %d\n", getuid());
64 snprintf(dirname, pos, "%s", src);
66 //D("src:[%s], dirname:[%s]\n", src, dirname);
67 int rc = smack_getlabel(dirname, &label_transmuted, SMACK_LABEL_TRANSMUTE);
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);
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);
86 D("fail to set label, is it transmuted?:%s\n", label_transmuted);
88 free(label_transmuted);
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);
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);
104 static int sync_send_label_notify(int s, const char *path, int success)
106 char buffer[512] = {0,};
107 snprintf(buffer, sizeof(buffer), "%d:%s", success, path);
109 int len = sdb_write(s, buffer, sizeof(buffer));
114 static void sync_read_label_notify(int s)
116 char buffer[512 + 1] = {0,};
119 int len = sdb_read(s, buffer, sizeof(buffer) - 1);
121 D("sync notify read errno:%d\n", errno);
125 if (buffer[0] == '0') {
126 D("sync notify child process exit\n");
133 //set_syncfile_smack_label(path);
137 static int mkdirs(int noti_fd, char *name)
153 ret = sdb_mkdir(name, DIR_PERMISSION);
155 sync_send_label_notify(noti_fd, name, 1);
157 if((ret < 0) && (errno != EEXIST)) {
158 E("mkdir(\"%s\") -> errno:%d\n", name, errno);
167 static int do_stat(int s, const char *path)
172 msg.stat.id = ID_STAT;
175 if(stat(path, &st)) {
179 D("failed to stat %s due to: errno:%d\n", path, errno);
181 msg.stat.mode = htoll(st.st_mode);
182 msg.stat.size = htoll(st.st_size);
183 msg.stat.time = htoll(st.st_mtime);
186 return writex(s, &msg.stat, sizeof(msg.stat));
189 static int do_list(int s, const char *path)
197 char tmp[1024 + 256 + 1];
200 char dirent_buffer[ sizeof(struct dirent) + 260 + 1 ] = {0,};
201 struct dirent *dirent_r = (struct dirent*)dirent_buffer;
204 memcpy(tmp, path, len);
206 fname = tmp + len + 1;
208 msg.dent.id = ID_DENT;
212 E("failed to open dir due to: errno:%d\n", errno);
216 while((readdir_r(d, dirent_r, &de) == 0) && de) {
217 int len = strlen(de->d_name);
219 /* not supposed to be possible, but
220 if it does happen, let's not buffer overrun */
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);
232 if(writex(s, &msg.dent, sizeof(msg.dent)) ||
233 writex(s, de->d_name, len)) {
243 msg.dent.id = ID_DONE;
247 msg.dent.namelen = 0;
248 return writex(s, &msg.dent, sizeof(msg.dent));
251 static int fail_message(int s, const char *reason)
254 size_t len = strlen(reason);
256 E("sync: failure: %s\n", reason);
258 msg.data.id = ID_FAIL;
259 msg.data.size = htoll(len);
261 if(writex(s, &msg.data, sizeof(msg.data)) ||
262 writex(s, reason, len)) {
269 static int fail_errno(int fd, int err_no)
272 char buf[512] = {0, };
274 ret_str = strerror_r(err_no, buf, sizeof(buf));
276 return fail_message(fd, (const char*)ret_str);
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")
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);
292 if (strstr(path, VAR_ABS_PATH) == path) {
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);
312 static int handle_send_file(int s, int noti_fd, char *path, mode_t mode, char *buffer)
315 unsigned int timestamp = 0;
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);
323 if(fd < 0 && errno == EEXIST) {
324 fd = sdb_open_mode(path, O_WRONLY, mode);
327 if(fail_errno(s, errno))
334 if(readx(s, &msg.data, sizeof(msg.data))) {
337 if(msg.data.id != ID_DATA) {
338 if(msg.data.id == ID_DONE) {
339 timestamp = ltohl(msg.data.size);
342 fail_message(s, "invalid data message");
345 len = ltohl(msg.data.size);
346 if(len > SYNC_DATA_MAX) {
347 fail_message(s, "oversize data message");
350 if(readx(s, buffer, len)) {
351 E("read failed due to unknown reason\n");
358 if(writex(fd, buffer, len)) {
359 int saved_errno = errno;
363 if(fail_errno(s, saved_errno)) return -1;
370 u.actime = timestamp;
371 u.modtime = timestamp;
374 msg.status.id = ID_OKAY;
375 msg.status.msglen = 0;
376 if(writex(s, &msg.status, sizeof(msg.status)))
378 // flush file system buffers due to N_SE-22305
381 E("sync error: %d!!!\n", fd);
384 sync_send_label_notify(noti_fd, path, 1);
396 static int handle_send_link(int s, int noti_fd, char *path, char *buffer)
402 if(readx(s, &msg.data, sizeof(msg.data)))
405 if(msg.data.id != ID_DATA) {
406 fail_message(s, "invalid data message: expected ID_DATA");
410 len = ltohl(msg.data.size);
411 if(len > SYNC_DATA_MAX) {
412 fail_message(s, "oversize data message");
415 if(readx(s, buffer, len))
418 ret = symlink(buffer, path);
419 if(ret && errno == ENOENT) {
420 mkdirs(noti_fd, path);
421 ret = symlink(buffer, path);
424 fail_errno(s, errno);
428 if(readx(s, &msg.data, sizeof(msg.data)))
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)))
437 fail_message(s, "invalid data message: expected ID_DONE");
443 #endif /* HAVE_SYMLINKS */
445 static int is_support_push()
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)));
451 static int is_support_pull()
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)));
457 static int do_send(int s, int noti_fd, char *path, char *buffer)
466 // Check the capability for file push support.
467 if(!is_support_push()) {
468 fail_message(s, "NO support file push.");
472 if (!request_validity_to_plugin(PLUGIN_SYNC_CMD_VERIFY_PUSH, path)) {
473 fail_message(s, "You cannot push files to this path.");
477 tmp = strrchr(path,',');
481 mode = strtoul(tmp + 1, NULL, 0);
483 is_link = S_ISLNK(mode);
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
490 mode = 0644; // set default permission value in most of unix system.
495 if (is_pkg_file_path(path)) {
502 // sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
508 ret = handle_send_link(s, noti_fd, path, buffer);
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.
518 //mode |= ((mode >> 3) & 0070);
519 //mode |= ((mode >> 3) & 0007);
520 ret = handle_send_file(s, noti_fd, path, mode, buffer);
526 static int do_recv(int s, const char *path, char *buffer)
531 // Check the capability for file push support.
532 if (!is_support_pull()) {
533 fail_message(s, "NO support file pull.");
537 if (!request_validity_to_plugin(PLUGIN_SYNC_CMD_VERIFY_PULL, path)) {
538 fail_message(s, "You cannot pull files from this path.");
542 fd = sdb_open(path, O_RDONLY);
544 if(fail_errno(s, errno)) return -1;
548 msg.data.id = ID_DATA;
550 r = sdb_read(fd, buffer, SYNC_DATA_MAX);
553 if(errno == EINTR) continue;
554 r = fail_errno(s, errno);
558 msg.data.size = htoll(r);
559 if(writex(s, &msg.data, sizeof(msg.data)) ||
560 writex(s, buffer, r)) {
568 msg.data.id = ID_DONE;
570 if(writex(s, &msg.data, sizeof(msg.data))) {
577 void file_sync_service(int fd, void *cookie)
583 struct timeval timeout;
587 if(sdb_socketpair(s)) {
588 E("cannot create service socket pair\n");
595 sdb_close(s[0]); //close the parent fd
596 sync_read_label_notify(s[1]);
598 } else if (pid > 0) {
601 char *buffer = malloc(SYNC_DATA_MAX);
606 if (should_drop_privileges()) {
607 if (set_sdk_user_privileges(DROP_CAPABILITIES_AFTER_FORK) < 0) {
611 set_root_privileges();
615 D("sync: waiting for command for %d sec\n", SYNC_TIMEOUT);
617 FD_ZERO(&set); /* clear the set */
618 FD_SET(fd, &set); /* add our file descriptor to the set */
620 timeout.tv_sec = SYNC_TIMEOUT;
623 rv = select(fd + 1, &set, NULL, NULL, &timeout);
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");
632 if(readx(fd, &msg.req, sizeof(msg.req))) {
633 fail_message(fd, "command read failure");
636 namelen = ltohl(msg.req.namelen);
638 fail_message(fd, "invalid namelen");
641 if(readx(fd, name, namelen)) {
642 fail_message(fd, "filename read failure");
649 D("sync: '%s' '%s'\n", (char*) &msg.req, name);
653 if(do_stat(fd, name)) goto fail;
656 if(do_list(fd, name)) goto fail;
659 if(do_send(fd, s[0], name, buffer)) goto fail;
662 if(do_recv(fd, name, buffer)) goto fail;
667 fail_message(fd, "unknown command");
681 sync_send_label_notify(s[0], name, 0);