276469d36822d7ca5f4a167577e40f568b49a1be
[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
31 #define TRACE_TAG  TRACE_SYNC
32 #include "sdb.h"
33 #include "file_sync_service.h"
34
35 #define SYNC_TIMEOUT 10
36
37 struct sync_permit_rule
38 {
39     const char *name;
40     const char *regx;
41     int mode; // 0:push, 1: pull, 2: push&push
42 };
43
44 struct sync_permit_rule sdk_sync_permit_rule[] = {
45 //    /* 0 */ {"rds", "^((/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/info/\\.sdk_delta\\.info$", 1},
46     /* 1 */ {"unitest", "^((/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/data/[a-zA-Z0-9_\\-]{1,50}\\.xml$", 1},
47     /* 2 */ {"codecoverage", "^((/opt/apps)|(/opt/usr/apps))/[a-zA-Z0-9]{10}/data/+([a-zA-Z0-9_/\\.])*+[a-zA-Z0-9_\\-\\.]{1,50}\\.gcda$", 1},
48     /* end */ {NULL, NULL, 0}
49 };
50
51 /* The typical default value for the umask is S_IWGRP | S_IWOTH (octal 022).
52  * Before use the DIR_PERMISSION, the process umask value should be set 0 using umask().
53  */
54 #define DIR_PERMISSION 0777
55
56 static int mkdirs(char *name)
57 {
58     int ret;
59     char *x = name + 1;
60
61     if(name[0] != '/') return -1;
62
63     for(;;) {
64         x = sdb_dirstart(x);
65         if(x == 0) return 0;
66         *x = 0;
67         /* tizen specific */
68         ret = sdb_mkdir(name, DIR_PERMISSION);
69
70         if((ret < 0) && (errno != EEXIST)) {
71             D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
72             *x = '/';
73             return ret;
74         }
75         *x++ = '/';
76     }
77     return 0;
78 }
79
80 static int do_stat(int s, const char *path)
81 {
82     syncmsg msg;
83     struct stat st;
84
85     msg.stat.id = ID_STAT;
86
87     /* follow link */
88     if(stat(path, &st)) {
89         msg.stat.mode = 0;
90         msg.stat.size = 0;
91         msg.stat.time = 0;
92         D("failed to stat %s due to: %s\n", path, strerror(errno));
93     } else {
94         msg.stat.mode = htoll(st.st_mode);
95         msg.stat.size = htoll(st.st_size);
96         msg.stat.time = htoll(st.st_mtime);
97     }
98
99     return writex(s, &msg.stat, sizeof(msg.stat));
100 }
101
102 static int do_list(int s, const char *path)
103 {
104     DIR *d;
105     struct dirent *de;
106     struct stat st;
107     syncmsg msg;
108     int len;
109
110     char tmp[1024 + 256 + 1];
111     char *fname;
112
113     len = strlen(path);
114     memcpy(tmp, path, len);
115     tmp[len] = '/';
116     fname = tmp + len + 1;
117
118     msg.dent.id = ID_DENT;
119
120     d = opendir(path);
121     if(d == NULL) {
122         D("failed to open dir due to: %s\n", strerror(errno));
123         goto done;
124     }
125
126     while((de = readdir(d))) {
127         int len = strlen(de->d_name);
128
129             /* not supposed to be possible, but
130                if it does happen, let's not buffer overrun */
131         if(len > 256) {
132             continue;
133         }
134
135         strcpy(fname, de->d_name);
136         if(lstat(tmp, &st) == 0) {
137             msg.dent.mode = htoll(st.st_mode);
138             msg.dent.size = htoll(st.st_size);
139             msg.dent.time = htoll(st.st_mtime);
140             msg.dent.namelen = htoll(len);
141
142             if(writex(s, &msg.dent, sizeof(msg.dent)) ||
143                writex(s, de->d_name, len)) {
144                 closedir(d);
145                 return -1;
146             }
147         }
148     }
149
150     closedir(d);
151
152 done:
153     msg.dent.id = ID_DONE;
154     msg.dent.mode = 0;
155     msg.dent.size = 0;
156     msg.dent.time = 0;
157     msg.dent.namelen = 0;
158     return writex(s, &msg.dent, sizeof(msg.dent));
159 }
160
161 static int fail_message(int s, const char *reason)
162 {
163     syncmsg msg;
164     int len = strlen(reason);
165
166     D("sync: failure: %s\n", reason);
167
168     msg.data.id = ID_FAIL;
169     msg.data.size = htoll(len);
170     if(writex(s, &msg.data, sizeof(msg.data)) ||
171        writex(s, reason, len)) {
172         return -1;
173     } else {
174         return 0;
175     }
176 }
177
178 static int fail_errno(int s)
179 {
180     return fail_message(s, strerror(errno));
181 }
182
183 static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
184 {
185     syncmsg msg;
186     unsigned int timestamp = 0;
187     int fd;
188
189     fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
190     if(fd < 0 && errno == ENOENT) {
191         mkdirs(path);
192         fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
193     }
194     if(fd < 0 && errno == EEXIST) {
195         fd = sdb_open_mode(path, O_WRONLY, mode);
196     }
197     if(fd < 0) {
198         if(fail_errno(s))
199             return -1;
200         fd = -1;
201     }
202     for(;;) {
203         unsigned int len;
204
205         if(readx(s, &msg.data, sizeof(msg.data))) {
206             goto fail;
207         }
208         if(msg.data.id != ID_DATA) {
209             if(msg.data.id == ID_DONE) {
210                 timestamp = ltohl(msg.data.size);
211                 break;
212             }
213             fail_message(s, "invalid data message");
214             goto fail;
215         }
216         len = ltohl(msg.data.size);
217         if(len > SYNC_DATA_MAX) {
218             fail_message(s, "oversize data message");
219             goto fail;
220         }
221         if(readx(s, buffer, len)) {
222             D("read failed due to unknown reason\n");
223             goto fail;
224         }
225
226         if(fd < 0) {
227             continue;
228         }
229         if(writex(fd, buffer, len)) {
230             int saved_errno = errno;
231             sdb_close(fd);
232             sdb_unlink(path);
233             fd = -1;
234             errno = saved_errno;
235             if(fail_errno(s)) return -1;
236         }
237     }
238
239     if(fd >= 0) {
240         struct utimbuf u;
241         sdb_close(fd);
242         u.actime = timestamp;
243         u.modtime = timestamp;
244         utime(path, &u);
245
246         msg.status.id = ID_OKAY;
247         msg.status.msglen = 0;
248         if(writex(s, &msg.status, sizeof(msg.status)))
249             return -1;
250         // flush file system buffers due to N_SE-22305
251         sync();
252     } else {
253         D("sync error: %d!!!\n", fd);
254         return -1;
255     }
256     return 0;
257
258 fail:
259     if(fd >= 0)
260         sdb_close(fd);
261     sdb_unlink(path);
262     return -1;
263 }
264
265 #ifdef HAVE_SYMLINKS
266 static int handle_send_link(int s, char *path, char *buffer)
267 {
268     syncmsg msg;
269     unsigned int len;
270     int ret;
271
272     if(readx(s, &msg.data, sizeof(msg.data)))
273         return -1;
274
275     if(msg.data.id != ID_DATA) {
276         fail_message(s, "invalid data message: expected ID_DATA");
277         return -1;
278     }
279
280     len = ltohl(msg.data.size);
281     if(len > SYNC_DATA_MAX) {
282         fail_message(s, "oversize data message");
283         return -1;
284     }
285     if(readx(s, buffer, len))
286         return -1;
287
288     ret = symlink(buffer, path);
289     if(ret && errno == ENOENT) {
290         mkdirs(path);
291         ret = symlink(buffer, path);
292     }
293     if(ret) {
294         fail_errno(s);
295         return -1;
296     }
297
298     if(readx(s, &msg.data, sizeof(msg.data)))
299         return -1;
300
301     if(msg.data.id == ID_DONE) {
302         msg.status.id = ID_OKAY;
303         msg.status.msglen = 0;
304         if(writex(s, &msg.status, sizeof(msg.status)))
305             return -1;
306     } else {
307         fail_message(s, "invalid data message: expected ID_DONE");
308         return -1;
309     }
310
311     return 0;
312 }
313 #endif /* HAVE_SYMLINKS */
314
315 static int do_send(int s, char *path, char *buffer)
316 {
317     char *tmp;
318     mode_t mode;
319     int is_link, ret;
320
321     tmp = strrchr(path,',');
322     if(tmp) {
323         *tmp = 0;
324         errno = 0;
325         mode = strtoul(tmp + 1, NULL, 0);
326 #ifndef HAVE_SYMLINKS
327         is_link = 0;
328 #else
329         is_link = S_ISLNK(mode);
330 #endif
331         // extracts file permission from stat.mode. (ex 100644 & 0777 = 644);
332         mode &= 0777; // combination of (S_IRWXU | S_IRWXG | S_IRWXO)
333     }
334     if(!tmp || errno) {
335         mode = 0644; // set default permission value in most of unix system.
336         is_link = 0;
337     }
338
339     // sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
340     sdb_unlink(path);
341
342
343 #ifdef HAVE_SYMLINKS
344     if(is_link)
345         ret = handle_send_link(s, path, buffer);
346     else {
347 #else
348     {
349 #endif
350         /* copy user permission bits to "group" and "other" permissions.
351          * ex) 0644 file will be created copied 0666 file.
352          * the following 2 lines should be commented if sdb process has been set to umask 0.
353          */
354
355         //mode |= ((mode >> 3) & 0070);
356         //mode |= ((mode >> 3) & 0007);
357         ret = handle_send_file(s, path, mode, buffer);
358     }
359
360     return ret;
361 }
362
363 static int do_recv(int s, const char *path, char *buffer)
364 {
365     syncmsg msg;
366     int fd, r;
367
368     fd = sdb_open(path, O_RDONLY);
369     if(fd < 0) {
370         if(fail_errno(s)) return -1;
371         return 0;
372     }
373
374     msg.data.id = ID_DATA;
375     for(;;) {
376         r = sdb_read(fd, buffer, SYNC_DATA_MAX);
377         if(r <= 0) {
378             if(r == 0) break;
379             if(errno == EINTR) continue;
380             r = fail_errno(s);
381             sdb_close(fd);
382             return r;
383         }
384         msg.data.size = htoll(r);
385         if(writex(s, &msg.data, sizeof(msg.data)) ||
386            writex(s, buffer, r)) {
387             sdb_close(fd);
388             return -1;
389         }
390     }
391
392     sdb_close(fd);
393
394     msg.data.id = ID_DONE;
395     msg.data.size = 0;
396     if(writex(s, &msg.data, sizeof(msg.data))) {
397         return -1;
398     }
399     return 0;
400 }
401
402 static int verify_sync_rule(const char* path) {
403     regex_t regex;
404     int ret;
405     char buf[PATH_MAX];
406     int i=0;
407
408     for (i=0; sdk_sync_permit_rule[i].regx != NULL; i++) {
409         ret = regcomp(&regex, sdk_sync_permit_rule[i].regx, REG_EXTENDED);
410         if(ret){
411             return 0;
412         }
413         // execute regular expression
414         ret = regexec(&regex, path, 0, NULL, 0);
415         if(!ret){
416             regfree(&regex);
417             D("found matched rule(%s) from %s path\n", sdk_sync_permit_rule[i].name, path);
418             return 1;
419         } else if( ret == REG_NOMATCH ){
420             // do nothin
421         } else{
422             regerror(ret, &regex, buf, sizeof(buf));
423             D("regex match failed(%s): %s\n",sdk_sync_permit_rule[i].name, buf);
424         }
425     }
426     regfree(&regex);
427     return 0;
428 }
429
430 void file_sync_service(int fd, void *cookie)
431 {
432     syncmsg msg;
433     char name[1025];
434     unsigned namelen;
435     fd_set set;
436     struct timeval timeout;
437     int rv;
438     char *buffer = malloc(SYNC_DATA_MAX);
439     if(buffer == 0) goto fail;
440
441     FD_ZERO(&set); /* clear the set */
442     FD_SET(fd, &set); /* add our file descriptor to the set */
443
444     timeout.tv_sec = SYNC_TIMEOUT;
445     timeout.tv_usec = 0;
446
447     for(;;) {
448         D("sync: waiting for command for %d sec\n", SYNC_TIMEOUT);
449
450         rv = select(fd + 1, &set, NULL, NULL, &timeout);
451         if (rv == -1) {
452             D("sync file descriptor select failed\n");
453         } else if (rv == 0) {
454             D("sync file descriptor timeout: (took %d sec over)\n", SYNC_TIMEOUT);
455             fail_message(fd, "sync timeout");
456             goto fail;
457         }
458
459         if(readx(fd, &msg.req, sizeof(msg.req))) {
460             fail_message(fd, "command read failure");
461             break;
462         }
463         namelen = ltohl(msg.req.namelen);
464         if(namelen > 1024) {
465             fail_message(fd, "invalid namelen");
466             break;
467         }
468         if(readx(fd, name, namelen)) {
469             fail_message(fd, "filename read failure");
470             break;
471         }
472         name[namelen] = 0;
473
474         msg.req.namelen = 0;
475         D("sync: '%s' '%s'\n", (char*) &msg.req, name);
476
477         if (should_drop_privileges() && !verify_sync_rule(name)) {
478             set_developer_privileges();
479         }
480
481         switch(msg.req.id) {
482         case ID_STAT:
483             if(do_stat(fd, name)) goto fail;
484             break;
485         case ID_LIST:
486             if(do_list(fd, name)) goto fail;
487             break;
488         case ID_SEND:
489             if(do_send(fd, name, buffer)) goto fail;
490             break;
491         case ID_RECV:
492             if(do_recv(fd, name, buffer)) goto fail;
493             break;
494         case ID_QUIT:
495             goto fail;
496         default:
497             fail_message(fd, "unknown command");
498             goto fail;
499         }
500     }
501
502 fail:
503     if(buffer != 0) free(buffer);
504     D("sync: done\n");
505     sdb_close(fd);
506 }