diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
commit | dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0 (patch) | |
tree | 2ba8d1a0846d69b18f623515e8d9b5d9fe38b590 /adb/file_sync_service.c | |
parent | e54eebbf1a908d65ee8cf80bab62821c05666d70 (diff) | |
download | core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.gz core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.bz2 core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'adb/file_sync_service.c')
-rw-r--r-- | adb/file_sync_service.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c new file mode 100644 index 000000000..a231e93d6 --- /dev/null +++ b/adb/file_sync_service.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <utime.h> + +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SYNC +#include "adb.h" +#include "file_sync_service.h" + +static int mkdirs(char *name) +{ + int ret; + char *x = name + 1; + + if(name[0] != '/') return -1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(name, 0775); + if((ret < 0) && (errno != EEXIST)) { + D("mkdir(\"%s\") -> %s\n", name, strerror(errno)); + *x = '/'; + return ret; + } + *x++ = '/'; + } + return 0; +} + +static int do_stat(int s, const char *path) +{ + syncmsg msg; + struct stat st; + + msg.stat.id = ID_STAT; + + if(lstat(path, &st)) { + msg.stat.mode = 0; + msg.stat.size = 0; + msg.stat.time = 0; + } else { + msg.stat.mode = htoll(st.st_mode); + msg.stat.size = htoll(st.st_size); + msg.stat.time = htoll(st.st_mtime); + } + + return writex(s, &msg.stat, sizeof(msg.stat)); +} + +static int do_list(int s, const char *path) +{ + DIR *d; + struct dirent *de; + struct stat st; + syncmsg msg; + int len; + + char tmp[1024 + 256 + 1]; + char *fname; + + len = strlen(path); + memcpy(tmp, path, len); + tmp[len] = '/'; + fname = tmp + len + 1; + + msg.dent.id = ID_DENT; + + d = opendir(path); + if(d == 0) 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; + + strcpy(fname, de->d_name); + if(lstat(tmp, &st) == 0) { + msg.dent.mode = htoll(st.st_mode); + msg.dent.size = htoll(st.st_size); + msg.dent.time = htoll(st.st_mtime); + msg.dent.namelen = htoll(len); + + if(writex(s, &msg.dent, sizeof(msg.dent)) || + writex(s, de->d_name, len)) { + return -1; + } + } + } + + closedir(d); + +done: + msg.dent.id = ID_DONE; + msg.dent.mode = 0; + msg.dent.size = 0; + msg.dent.time = 0; + msg.dent.namelen = 0; + return writex(s, &msg.dent, sizeof(msg.dent)); +} + +static int fail_message(int s, const char *reason) +{ + syncmsg msg; + int len = strlen(reason); + + D("sync: failure: %s\n", reason); + + msg.data.id = ID_FAIL; + msg.data.size = htoll(len); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, reason, len)) { + return -1; + } else { + return 0; + } +} + +static int fail_errno(int s) +{ + return fail_message(s, strerror(errno)); +} + +static int handle_send_file(int s, char *path, mode_t mode, char *buffer) +{ + syncmsg msg; + unsigned int timestamp = 0; + int fd; + + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + if(fd < 0 && errno == ENOENT) { + mkdirs(path); + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if(fd < 0 && errno == EEXIST) { + fd = adb_open_mode(path, O_WRONLY, mode); + } + if(fd < 0) { + if(fail_errno(s)) + return -1; + fd = -1; + } + + for(;;) { + unsigned int len; + + 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); + break; + } + fail_message(s, "invalid data message"); + goto fail; + } + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + goto fail; + } + if(readx(s, buffer, len)) + goto fail; + + if(fd < 0) + continue; + if(writex(fd, buffer, len)) { + adb_close(fd); + adb_unlink(path); + fd = -1; + if(fail_errno(s)) return -1; + } + } + + if(fd >= 0) { + struct utimbuf u; + adb_close(fd); + u.actime = timestamp; + u.modtime = timestamp; + utime(path, &u); + + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } + return 0; + +fail: + if(fd >= 0) + adb_close(fd); + adb_unlink(path); + return -1; +} + +#ifdef HAVE_SYMLINKS +static int handle_send_link(int s, char *path, char *buffer) +{ + syncmsg msg; + unsigned int len; + int ret; + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id != ID_DATA) { + fail_message(s, "invalid data message: expected ID_DATA"); + return -1; + } + + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + return -1; + } + if(readx(s, buffer, len)) + return -1; + + ret = symlink(buffer, path); + if(ret && errno == ENOENT) { + mkdirs(path); + ret = symlink(buffer, path); + } + if(ret) { + fail_errno(s); + return -1; + } + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id == ID_DONE) { + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } else { + fail_message(s, "invalid data message: expected ID_DONE"); + return -1; + } + + return 0; +} +#endif /* HAVE_SYMLINKS */ + +static int do_send(int s, char *path, char *buffer) +{ + char *tmp; + mode_t mode; + int is_link, ret; + + tmp = strrchr(path,','); + if(tmp) { + *tmp = 0; + errno = 0; + mode = strtoul(tmp + 1, NULL, 0); +#ifndef HAVE_SYMLINKS + is_link = 0; +#else + is_link = S_ISLNK(mode); +#endif + mode &= 0777; + } + if(!tmp || errno) { + mode = 0644; + is_link = 0; + } + + adb_unlink(path); + + +#ifdef HAVE_SYMLINKS + if(is_link) + ret = handle_send_link(s, path, buffer); + else { +#else + { +#endif + /* copy user permission bits to "group" and "other" permissions */ + mode |= ((mode >> 3) & 0070); + mode |= ((mode >> 3) & 0007); + + ret = handle_send_file(s, path, mode, buffer); + } + + return ret; +} + +static int do_recv(int s, const char *path, char *buffer) +{ + syncmsg msg; + int fd, r; + + fd = adb_open(path, O_RDONLY); + if(fd < 0) { + if(fail_errno(s)) return -1; + return 0; + } + + msg.data.id = ID_DATA; + for(;;) { + r = adb_read(fd, buffer, SYNC_DATA_MAX); + if(r <= 0) { + if(r == 0) break; + if(errno == EINTR) continue; + r = fail_errno(s); + adb_close(fd); + return r; + } + msg.data.size = htoll(r); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, buffer, r)) { + adb_close(fd); + return -1; + } + } + + adb_close(fd); + + msg.data.id = ID_DONE; + msg.data.size = 0; + if(writex(s, &msg.data, sizeof(msg.data))) { + return -1; + } + + return 0; +} + +void file_sync_service(int fd, void *cookie) +{ + syncmsg msg; + char name[1025]; + unsigned namelen; + + char *buffer = malloc(SYNC_DATA_MAX); + if(buffer == 0) goto fail; + + for(;;) { + D("sync: waiting for command\n"); + + 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; + } + } + +fail: + if(buffer != 0) free(buffer); + D("sync: done\n"); + adb_close(fd); +} |