/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
#include <sys/types.h>
#include <dirent.h>
#include <utime.h>
-
+#include <regex.h>
#include <errno.h>
-
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/smack.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_SYNC
#include "sdb.h"
#include "file_sync_service.h"
+#include "sdktools.h"
+#include "utils.h"
+
+#define SYNC_TIMEOUT 15
+
+struct sync_permit_rule
+{
+ const char *name;
+ const char *regx;
+ int mode; // 0:push, 1: pull, 2: push&push
+};
+
+struct sync_permit_rule sdk_sync_permit_rule[] = {
+// /* 0 */ {"rds", "^((/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/info/\\.sdk_delta\\.info$", 1},
+ /* 1 */ {"unitest", "^((/tmp)|(/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/data/[a-zA-Z0-9_\\-]{1,50}\\.xml$", 1},
+ /* 2 */ {"codecoverage", "^((/tmp)|(/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/data/+(.)*\\.gcda$", 1},
+ /* 3 */ {"da", "^(/tmp/da/)*+[a-zA-Z0-9_\\-\\.]{1,50}\\.png$", 1},
+ /* end */ {NULL, NULL, 0}
+};
/* The typical default value for the umask is S_IWGRP | S_IWOTH (octal 022).
* Before use the DIR_PERMISSION, the process umask value should be set 0 using umask().
*/
#define DIR_PERMISSION 0777
-static int mkdirs(char *name)
+int is_install_pkg_file = 0;
+
+static void set_syncfile_smack_label(char *src) {
+ char *label_transmuted = NULL;
+ char *label = NULL;
+ char *src_chr = strrchr(src, '/');
+ int pos = src_chr - src + 1;
+ char dirname[512];
+
+ if (getuid() != 0) {
+ D("need root permission to set smack label: %d\n", getuid());
+ return;
+ }
+
+ snprintf(dirname, pos, "%s", src);
+
+ //D("src:[%s], dirname:[%s]\n", src, dirname);
+ int rc = smack_getlabel(dirname, &label_transmuted, SMACK_LABEL_TRANSMUTE);
+
+ if (rc == 0 && label_transmuted != NULL) {
+ if (!strcmp("TRUE", label_transmuted)) {
+ rc = smack_getlabel(dirname, &label, SMACK_LABEL_ACCESS);
+ if (rc == 0 && label != NULL) {
+ if (smack_setlabel(src, label, SMACK_LABEL_ACCESS) == -1) {
+ D("unable to set sync file smack label %s due to %s\n", label, strerror(errno));
+ }
+ free(label);
+ }
+ } else{
+ D("fail to set label, is it transmuted?:%s\n", label_transmuted);
+ }
+ free(label_transmuted);
+ } else {
+ if (smack_setlabel(src, SMACK_SYNC_FILE_LABEL, SMACK_LABEL_ACCESS) == -1) {
+ D("unable to set sync file smack label %s due to (errno:%d)\n", SMACK_SYNC_FILE_LABEL, errno);
+ }
+ }
+}
+
+static int sync_send_label_notify(int s, const char *path, int success)
+{
+ char buffer[512] = {0,};
+ snprintf(buffer, sizeof(buffer), "%d:%s", success, path);
+
+ int len = sdb_write(s, buffer, sizeof(buffer));
+
+ return len;
+}
+
+static void sync_read_label_notify(int s)
+{
+ char buffer[512] = {0,};
+
+ while (1) {
+ int len = sdb_read(s, buffer, sizeof(buffer));
+ if (len < 0) {
+ D("sync notify read error : (errno:%d)\n", errno);
+ exit(-1);
+ }
+
+ if (buffer[0] == '0') {
+ D("sync notify child process exit\n");
+ exit(-1);
+ }
+ buffer[511] = '\0';
+ char *path = buffer;
+ path++;
+ path++;
+ set_syncfile_smack_label(path);
+ }
+}
+
+static int mkdirs(int noti_fd, char *name)
{
int ret;
char *x = name + 1;
- if(name[0] != '/') return -1;
+ if(name[0] != '/') {
+ return -1;
+ }
for(;;) {
x = sdb_dirstart(x);
- if(x == 0) return 0;
+ if(x == 0) {
+ return 0;
+ }
*x = 0;
+
ret = sdb_mkdir(name, DIR_PERMISSION);
+ if (ret == 0) {
+ sync_send_label_notify(noti_fd, name, 1);
+ }
if((ret < 0) && (errno != EEXIST)) {
- D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
+ D("mkdir(\"%s\") -> (errno:%d)\n", name, errno);
*x = '/';
return ret;
}
msg.stat.id = ID_STAT;
- if(lstat(path, &st)) {
+ /* follow link */
+ if(stat(path, &st)) {
msg.stat.mode = 0;
msg.stat.size = 0;
msg.stat.time = 0;
+ D("failed to stat %s due to: (errno:%d)\n", path, errno);
} else {
msg.stat.mode = htoll(st.st_mode);
msg.stat.size = htoll(st.st_size);
msg.dent.id = ID_DENT;
d = opendir(path);
- if(d == 0) goto done;
+ if(d == NULL) {
+ D("failed to open dir due to: (errno:%d)\n", errno);
+ goto done;
+ }
while((de = readdir(d))) {
int len = strlen(de->d_name);
/* not supposed to be possible, but
if it does happen, let's not buffer overrun */
- if(len > 256) continue;
+ if(len > 256) {
+ continue;
+ }
- strcpy(fname, de->d_name);
+ s_strncpy(fname, de->d_name, sizeof tmp);
if(lstat(tmp, &st) == 0) {
msg.dent.mode = htoll(st.st_mode);
msg.dent.size = htoll(st.st_size);
if(writex(s, &msg.dent, sizeof(msg.dent)) ||
writex(s, de->d_name, len)) {
+ closedir(d);
return -1;
}
}
static int fail_errno(int s)
{
- return fail_message(s, strerror(errno));
+ char err_msg[20] = {0, };
+ snprintf(err_msg, sizeof(err_msg), "(errno:%d)", errno);
+ return fail_message(s, err_msg);
}
-static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
+// FIXME: should get the following paths with api later but, do it for simple and not having dependency on other packages
+#define CMD_MEDIADB_UPDATE "/usr/bin/mediadb-update"
+#define MEDIA_CONTENTS_PATH1 "/opt/media"
+#define MEDIA_CONTENTS_PATH2 "/opt/usr/media"
+#define MEDIA_CONTENTS_PATH3 "/opt/storage/sdcard"
+
+static void sync_mediadb(char *path) {
+ if (access(CMD_MEDIADB_UPDATE, F_OK) != 0) {
+ D("%s: command not found\n", CMD_MEDIADB_UPDATE);
+ return;
+ }
+
+ if (strstr(path, MEDIA_CONTENTS_PATH1) != NULL) {
+ char *arg_list[] = {CMD_MEDIADB_UPDATE, "r", MEDIA_CONTENTS_PATH1, NULL};
+
+ spawn(CMD_MEDIADB_UPDATE, arg_list);
+ D("media db update done to %s\n", MEDIA_CONTENTS_PATH1);
+ } else if (strstr(path, MEDIA_CONTENTS_PATH2) != NULL) {
+ char *arg_list[] = {CMD_MEDIADB_UPDATE, "r", MEDIA_CONTENTS_PATH2, NULL};
+
+ spawn(CMD_MEDIADB_UPDATE, arg_list);
+ D("media db update done to %s\n", MEDIA_CONTENTS_PATH2);
+ } else if (strstr(path, MEDIA_CONTENTS_PATH3) != NULL) {
+ char *arg_list[] = {CMD_MEDIADB_UPDATE, "r", MEDIA_CONTENTS_PATH3, NULL};
+
+ spawn(CMD_MEDIADB_UPDATE, arg_list);
+ D("media db update done to %s\n", MEDIA_CONTENTS_PATH3);
+ }
+ return;
+}
+
+static int handle_send_file(int s, int noti_fd, char *path, mode_t mode, char *buffer)
{
syncmsg msg;
unsigned int timestamp = 0;
fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
if(fd < 0 && errno == ENOENT) {
- mkdirs(path);
+ mkdirs(noti_fd, path);
fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
if(fd < 0 && errno == EEXIST) {
return -1;
fd = -1;
}
-
for(;;) {
unsigned int len;
- if(readx(s, &msg.data, sizeof(msg.data)))
+ if(readx(s, &msg.data, sizeof(msg.data))) {
goto fail;
-
+ }
if(msg.data.id != ID_DATA) {
if(msg.data.id == ID_DONE) {
timestamp = ltohl(msg.data.size);
fail_message(s, "oversize data message");
goto fail;
}
- if(readx(s, buffer, len))
+ if(readx(s, buffer, len)) {
+ D("read failed due to unknown reason\n");
goto fail;
+ }
- if(fd < 0)
+ if(fd < 0) {
continue;
+ }
if(writex(fd, buffer, len)) {
+ int saved_errno = errno;
sdb_close(fd);
sdb_unlink(path);
fd = -1;
+ errno = saved_errno;
if(fail_errno(s)) return -1;
}
}
msg.status.msglen = 0;
if(writex(s, &msg.status, sizeof(msg.status)))
return -1;
+ // flush file system buffers due to N_SE-22305
+ sync();
+ } else {
+ D("sync error: %d!!!\n", fd);
+ return -1;
}
+ sync_send_label_notify(noti_fd, path, 1);
+ sync_mediadb(path);
return 0;
fail:
}
#ifdef HAVE_SYMLINKS
-static int handle_send_link(int s, char *path, char *buffer)
+static int handle_send_link(int s, int noti_fd, char *path, char *buffer)
{
syncmsg msg;
unsigned int len;
ret = symlink(buffer, path);
if(ret && errno == ENOENT) {
- mkdirs(path);
+ mkdirs(noti_fd, path);
ret = symlink(buffer, path);
}
if(ret) {
}
#endif /* HAVE_SYMLINKS */
-static int do_send(int s, char *path, char *buffer)
+static int do_send(int s, int noti_fd, char *path, char *buffer)
{
char *tmp;
mode_t mode;
#endif
// extracts file permission from stat.mode. (ex 100644 & 0777 = 644);
mode &= 0777; // combination of (S_IRWXU | S_IRWXG | S_IRWXO)
+ mode |= S_IWOTH; // SDK requirement from N_SE-43337
}
if(!tmp || errno) {
mode = 0644; // set default permission value in most of unix system.
is_link = 0;
}
+ if(is_install_pkg_file) {
+ mode = 0444;
+ is_link = 0;
+ }
// sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
sdb_unlink(path);
#ifdef HAVE_SYMLINKS
if(is_link)
- ret = handle_send_link(s, path, buffer);
+ ret = handle_send_link(s, noti_fd, path, buffer);
else {
#else
{
//mode |= ((mode >> 3) & 0070);
//mode |= ((mode >> 3) & 0007);
-
- ret = handle_send_file(s, path, mode, buffer);
+ ret = handle_send_file(s, noti_fd, path, mode, buffer);
}
return ret;
if(writex(s, &msg.data, sizeof(msg.data))) {
return -1;
}
+ return 0;
+}
+
+static int verify_sync_rule(const char* path) {
+ regex_t regex;
+ int ret;
+ char buf[PATH_MAX];
+ int i=0;
+
+ // to prevent hijacking the pkg file.
+ if (is_pkg_file_path(path)) {
+ is_install_pkg_file = 1;
+ D("matched pkg file path: to prevent hijacking the pkg file\n");
+ return 1;
+ }
+ for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++) {
+ ret = regcomp(®ex, sdk_sync_permit_rule[i].regx, REG_EXTENDED);
+ if(ret){
+ return 0;
+ }
+ // execute regular expression
+ ret = regexec(®ex, path, 0, NULL, 0);
+ if(!ret){
+ regfree(®ex);
+ D("found matched rule(%s) from %s path\n", sdk_sync_permit_rule[i].name, path);
+ return 1;
+ } else if( ret == REG_NOMATCH ){
+ // do nothin
+ } else{
+ regerror(ret, ®ex, buf, sizeof(buf));
+ D("regex match failed(%s): %s\n",sdk_sync_permit_rule[i].name, buf);
+ }
+ }
+ regfree(®ex);
return 0;
}
syncmsg msg;
char name[1025];
unsigned namelen;
-
+ fd_set set;
+ struct timeval timeout;
+ int rv;
+ int s[2];
+ is_install_pkg_file = 0;
+
+ if(sdb_socketpair(s)) {
+ D("cannot create service socket pair\n");
+ exit(-1);
+ }
char *buffer = malloc(SYNC_DATA_MAX);
- if(buffer == 0) goto fail;
+ if(buffer == 0) {
+ goto fail;
+ }
- for(;;) {
- D("sync: waiting for command\n");
+ FD_ZERO(&set); /* clear the set */
+ FD_SET(fd, &set); /* add our file descriptor to the set */
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ sdb_close(s[0]); //close the parent fd
+ sync_read_label_notify(s[1]);
+ } else if (pid > 0) {
+ sdb_close(s[1]);
+ for(;;) {
+ D("sync: waiting for command for %d sec\n", SYNC_TIMEOUT);
+
+ // in Linux, timeout would be altered after each select() call.
+ // Therefore, re-initialization is needed.
+ timeout.tv_sec = SYNC_TIMEOUT;
+ timeout.tv_usec = 0;
+ rv = select(fd + 1, &set, NULL, NULL, &timeout);
+ if (rv == -1) {
+ D("sync file descriptor select failed\n");
+ } else if (rv == 0) {
+ D("sync file descriptor timeout: (took %d sec over)\n", SYNC_TIMEOUT);
+ fail_message(fd, "sync timeout");
+ goto fail;
+ }
- if(readx(fd, &msg.req, sizeof(msg.req))) {
- fail_message(fd, "command read failure");
- break;
- }
- namelen = ltohl(msg.req.namelen);
- if(namelen > 1024) {
- fail_message(fd, "invalid namelen");
- break;
- }
- if(readx(fd, name, namelen)) {
- fail_message(fd, "filename read failure");
- break;
- }
- name[namelen] = 0;
-
- msg.req.namelen = 0;
- D("sync: '%s' '%s'\n", (char*) &msg.req, name);
-
- switch(msg.req.id) {
- case ID_STAT:
- if(do_stat(fd, name)) goto fail;
- break;
- case ID_LIST:
- if(do_list(fd, name)) goto fail;
- break;
- case ID_SEND:
- if(do_send(fd, name, buffer)) goto fail;
- break;
- case ID_RECV:
- if(do_recv(fd, name, buffer)) goto fail;
- break;
- case ID_QUIT:
- goto fail;
- default:
- fail_message(fd, "unknown command");
- goto fail;
+ if(readx(fd, &msg.req, sizeof(msg.req))) {
+ fail_message(fd, "command read failure");
+ break;
+ }
+ namelen = ltohl(msg.req.namelen);
+ if(namelen > 1024) {
+ fail_message(fd, "invalid namelen");
+ break;
+ }
+ if(readx(fd, name, namelen)) {
+ fail_message(fd, "filename read failure");
+ break;
+ }
+ name[namelen] = 0;
+
+ msg.req.namelen = 0;
+ D("sync: '%s' '%s'\n", (char*) &msg.req, name);
+
+ if (should_drop_privileges() && !verify_sync_rule(name)) {
+ set_developer_privileges();
+ }
+
+ switch(msg.req.id) {
+ case ID_STAT:
+ if(do_stat(fd, name)) goto fail;
+ break;
+ case ID_LIST:
+ if(do_list(fd, name)) goto fail;
+ break;
+ case ID_SEND:
+ if(do_send(fd, s[0], name, buffer)) goto fail;
+ break;
+ case ID_RECV:
+ if(do_recv(fd, name, buffer)) goto fail;
+ break;
+ case ID_QUIT:
+ goto fail;
+ default:
+ fail_message(fd, "unknown command");
+ goto fail;
+ }
}
}
+
fail:
- if(buffer != 0) free(buffer);
+ if(buffer != 0) {
+ free(buffer);
+ }
D("sync: done\n");
+ sync_send_label_notify(s[0], name, 0);
+ sdb_close(s[0]);
+ sdb_close(s[1]);
sdb_close(fd);
}