file_sync_client.cpp revision 05786779f390b23cc8611b101458cd174cf4e938
1/*
2 * Copyright (C) 2007 The Android Open Source Project
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 <dirent.h>
18#include <errno.h>
19#include <inttypes.h>
20#include <limits.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/time.h>
26#include <sys/types.h>
27#include <time.h>
28#include <unistd.h>
29#include <utime.h>
30
31#include <memory>
32#include <vector>
33
34#include "sysdeps.h"
35
36#include "adb.h"
37#include "adb_client.h"
38#include "adb_io.h"
39#include "adb_utils.h"
40#include "file_sync_service.h"
41#include "line_printer.h"
42
43#include <base/file.h>
44#include <base/strings.h>
45#include <base/stringprintf.h>
46
47struct syncsendbuf {
48    unsigned id;
49    unsigned size;
50    char data[SYNC_DATA_MAX];
51};
52
53class SyncConnection {
54  public:
55    SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
56        max = SYNC_DATA_MAX; // TODO: decide at runtime.
57
58        std::string error;
59        fd = adb_connect("sync:", &error);
60        if (fd < 0) {
61            Error("connect failed: %s", error.c_str());
62        }
63    }
64
65    ~SyncConnection() {
66        if (!IsValid()) return;
67
68        if (SendQuit()) {
69            // We sent a quit command, so the server should be doing orderly
70            // shutdown soon. But if we encountered an error while we were using
71            // the connection, the server might still be sending data (before
72            // doing orderly shutdown), in which case we won't wait for all of
73            // the data nor the coming orderly shutdown. In the common success
74            // case, this will wait for the server to do orderly shutdown.
75            ReadOrderlyShutdown(fd);
76        }
77        adb_close(fd);
78    }
79
80    bool IsValid() { return fd >= 0; }
81
82    bool SendRequest(int id, const char* path_and_mode) {
83        size_t path_length = strlen(path_and_mode);
84        if (path_length > 1024) {
85            Error("SendRequest failed: path too long: %zu", path_length);
86            errno = ENAMETOOLONG;
87            return false;
88        }
89
90        // Sending header and payload in a single write makes a noticeable
91        // difference to "adb sync" performance.
92        std::vector<char> buf(sizeof(SyncRequest) + path_length);
93        SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
94        req->id = id;
95        req->path_length = path_length;
96        char* data = reinterpret_cast<char*>(req + 1);
97        memcpy(data, path_and_mode, path_length);
98
99        return WriteFdExactly(fd, &buf[0], buf.size());
100    }
101
102    // Sending header, payload, and footer in a single write makes a huge
103    // difference to "adb sync" performance.
104    bool SendSmallFile(const char* path_and_mode,
105                       const char* rpath,
106                       const char* data, size_t data_length,
107                       unsigned mtime) {
108        Print(rpath);
109
110        size_t path_length = strlen(path_and_mode);
111        if (path_length > 1024) {
112            Error("SendSmallFile failed: path too long: %zu", path_length);
113            errno = ENAMETOOLONG;
114            return false;
115        }
116
117        std::vector<char> buf(sizeof(SyncRequest) + path_length +
118                 sizeof(SyncRequest) + data_length +
119                 sizeof(SyncRequest));
120        char* p = &buf[0];
121
122        SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
123        req_send->id = ID_SEND;
124        req_send->path_length = path_length;
125        p += sizeof(SyncRequest);
126        memcpy(p, path_and_mode, path_length);
127        p += path_length;
128
129        SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
130        req_data->id = ID_DATA;
131        req_data->path_length = data_length;
132        p += sizeof(SyncRequest);
133        memcpy(p, data, data_length);
134        p += data_length;
135
136        SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
137        req_done->id = ID_DONE;
138        req_done->path_length = mtime;
139        p += sizeof(SyncRequest);
140
141        if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
142
143        total_bytes += data_length;
144        return true;
145    }
146
147    bool CopyDone(const char* from, const char* to) {
148        syncmsg msg;
149        if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
150            Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
151            return false;
152        }
153        if (msg.status.id == ID_OKAY) {
154            return true;
155        }
156        if (msg.status.id != ID_FAIL) {
157            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
158            return false;
159        }
160        return ReportCopyFailure(from, to, msg);
161    }
162
163    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
164        std::vector<char> buf(msg.status.msglen + 1);
165        if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
166            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
167                  from, to, strerror(errno));
168            return false;
169        }
170        buf[msg.status.msglen] = 0;
171        Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
172        return false;
173    }
174
175    std::string TransferRate() {
176        uint64_t ms = CurrentTimeMs() - start_time_ms_;
177        if (total_bytes == 0 || ms == 0) return "";
178
179        double s = static_cast<double>(ms) / 1000LL;
180        double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
181        return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
182                                           rate, total_bytes, s);
183    }
184
185    void Print(const std::string& s) {
186        // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
187        line_printer_.Print(s, LinePrinter::ELIDE);
188    }
189
190    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
191        std::string s = "adb: error: ";
192
193        va_list ap;
194        va_start(ap, fmt);
195        android::base::StringAppendV(&s, fmt, ap);
196        va_end(ap);
197
198        line_printer_.Print(s, LinePrinter::FULL);
199    }
200
201    uint64_t total_bytes;
202
203    // TODO: add a char[max] buffer here, to replace syncsendbuf...
204    int fd;
205    size_t max;
206
207  private:
208    uint64_t start_time_ms_;
209
210    LinePrinter line_printer_;
211
212    bool SendQuit() {
213        return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
214    }
215
216    static uint64_t CurrentTimeMs() {
217        struct timeval tv;
218        gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
219        return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
220    }
221};
222
223typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
224
225static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
226    if (!sc.SendRequest(ID_LIST, path)) return false;
227
228    while (true) {
229        syncmsg msg;
230        if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
231
232        if (msg.dent.id == ID_DONE) return true;
233        if (msg.dent.id != ID_DENT) return false;
234
235        size_t len = msg.dent.namelen;
236        if (len > 256) return false; // TODO: resize buffer? continue?
237
238        char buf[257];
239        if (!ReadFdExactly(sc.fd, buf, len)) return false;
240        buf[len] = 0;
241
242        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
243    }
244}
245
246static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
247                             unsigned int* mode, unsigned int* size) {
248    syncmsg msg;
249    if (!ReadFdExactly(sc.fd, &msg.stat, sizeof(msg.stat)) || msg.stat.id != ID_STAT) {
250        return false;
251    }
252
253    if (timestamp) *timestamp = msg.stat.time;
254    if (mode) *mode = msg.stat.mode;
255    if (size) *size = msg.stat.size;
256
257    return true;
258}
259
260static bool sync_stat(SyncConnection& sc, const char* path,
261                      unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
262    return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
263}
264
265static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
266                          const char* lpath, const char* rpath,
267                          unsigned mtime) {
268    if (!sc.SendRequest(ID_SEND, path_and_mode)) {
269        sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
270        return false;
271    }
272
273    struct stat st;
274    if (stat(lpath, &st) == -1) {
275        sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
276        return false;
277    }
278
279    uint64_t total_size = st.st_size;
280    uint64_t bytes_copied = 0;
281
282    int lfd = adb_open(lpath, O_RDONLY);
283    if (lfd < 0) {
284        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
285        return false;
286    }
287
288    syncsendbuf sbuf;
289    sbuf.id = ID_DATA;
290    while (true) {
291        int ret = adb_read(lfd, sbuf.data, sc.max);
292        if (ret <= 0) {
293            if (ret < 0) {
294                sc.Error("cannot read '%s': %s", lpath, strerror(errno));
295                adb_close(lfd);
296                return false;
297            }
298            break;
299        }
300
301        sbuf.size = ret;
302        if (!WriteFdExactly(sc.fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
303            adb_close(lfd);
304            return false;
305        }
306        sc.total_bytes += ret;
307
308        bytes_copied += ret;
309
310        int percentage = static_cast<int>(bytes_copied * 100 / total_size);
311        sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
312    }
313
314    adb_close(lfd);
315
316    syncmsg msg;
317    msg.data.id = ID_DONE;
318    msg.data.size = mtime;
319    if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
320        sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
321        return false;
322    }
323
324    return true;
325}
326
327static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
328                      unsigned mtime, mode_t mode)
329{
330    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
331
332    if (S_ISLNK(mode)) {
333#if !defined(_WIN32)
334        char buf[PATH_MAX];
335        ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
336        if (data_length == -1) {
337            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
338            return false;
339        }
340        buf[data_length++] = '\0';
341
342        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
343        return sc.CopyDone(lpath, rpath);
344#endif
345    }
346
347    if (!S_ISREG(mode)) {
348        sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
349        return false;
350    }
351
352    struct stat st;
353    if (stat(lpath, &st) == -1) {
354        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
355        return false;
356    }
357    if (st.st_size < SYNC_DATA_MAX) {
358        std::string data;
359        if (!android::base::ReadFileToString(lpath, &data)) {
360            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
361            return false;
362        }
363        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
364            return false;
365        }
366    } else {
367        if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
368            return false;
369        }
370    }
371    return sc.CopyDone(lpath, rpath);
372}
373
374static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
375    sc.Print(rpath);
376
377    unsigned size = 0;
378    if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
379
380    if (!sc.SendRequest(ID_RECV, rpath)) return false;
381
382    adb_unlink(lpath);
383    mkdirs(lpath);
384    int lfd = adb_creat(lpath, 0644);
385    if (lfd < 0) {
386        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
387        return false;
388    }
389
390    uint64_t bytes_copied = 0;
391    while (true) {
392        syncmsg msg;
393        if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
394            adb_close(lfd);
395            adb_unlink(lpath);
396            return false;
397        }
398
399        if (msg.data.id == ID_DONE) break;
400
401        if (msg.data.id != ID_DATA) {
402            adb_close(lfd);
403            adb_unlink(lpath);
404            sc.ReportCopyFailure(rpath, lpath, msg);
405            return false;
406        }
407
408        if (msg.data.size > sc.max) {
409            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
410            adb_close(lfd);
411            adb_unlink(lpath);
412            return false;
413        }
414
415        char buffer[SYNC_DATA_MAX];
416        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
417            adb_close(lfd);
418            adb_unlink(lpath);
419            return false;
420        }
421
422        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
423            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
424            adb_close(lfd);
425            adb_unlink(lpath);
426            return false;
427        }
428
429        sc.total_bytes += msg.data.size;
430
431        bytes_copied += msg.data.size;
432
433        int percentage = static_cast<int>(bytes_copied * 100 / size);
434        sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
435    }
436
437    adb_close(lfd);
438    return true;
439}
440
441static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
442                          const char* name, void* /*cookie*/) {
443    printf("%08x %08x %08x %s\n", mode, size, time, name);
444}
445
446bool do_sync_ls(const char* path) {
447    SyncConnection sc;
448    if (!sc.IsValid()) return false;
449
450    return sync_ls(sc, path, do_sync_ls_cb, 0);
451}
452
453struct copyinfo
454{
455    copyinfo *next;
456    const char *src;
457    const char *dst;
458    unsigned int time;
459    unsigned int mode;
460    uint64_t size;
461    int flag;
462};
463
464static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
465    int slen = strlen(spath);
466    int dlen = strlen(dpath);
467    int nlen = strlen(name);
468    int ssize = slen + nlen + 2;
469    int dsize = dlen + nlen + 2;
470
471    copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
472    if (ci == 0) {
473        fprintf(stderr, "out of memory\n");
474        abort();
475    }
476
477    ci->next = 0;
478    ci->time = 0;
479    ci->mode = 0;
480    ci->size = 0;
481    ci->flag = 0;
482    ci->src = (const char*)(ci + 1);
483    ci->dst = ci->src + ssize;
484    snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
485    snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
486
487    return ci;
488}
489
490static bool IsDotOrDotDot(const char* name) {
491    return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
492}
493
494static int local_build_list(SyncConnection& sc,
495                            copyinfo** filelist, const char* lpath, const char* rpath) {
496    copyinfo *dirlist = 0;
497    copyinfo *ci, *next;
498
499    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
500    if (!dir) {
501        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
502        return -1;
503    }
504
505    dirent* de;
506    while ((de = readdir(dir.get()))) {
507        if (IsDotOrDotDot(de->d_name)) continue;
508
509        char stat_path[PATH_MAX];
510        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
511            sc.Error("skipping long path '%s%s'", lpath, de->d_name);
512            continue;
513        }
514        strcpy(stat_path, lpath);
515        strcat(stat_path, de->d_name);
516
517        struct stat st;
518        if (!lstat(stat_path, &st)) {
519            if (S_ISDIR(st.st_mode)) {
520                ci = mkcopyinfo(lpath, rpath, de->d_name, 1);
521                ci->next = dirlist;
522                dirlist = ci;
523            } else {
524                ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
525                if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
526                    sc.Error("skipping special file '%s'", ci->src);
527                    free(ci);
528                } else {
529                    ci->time = st.st_mtime;
530                    ci->mode = st.st_mode;
531                    ci->size = st.st_size;
532                    ci->next = *filelist;
533                    *filelist = ci;
534                }
535            }
536        } else {
537            sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno));
538        }
539    }
540
541    // Close this directory and recurse.
542    dir.reset();
543    for (ci = dirlist; ci != 0; ci = next) {
544        next = ci->next;
545        local_build_list(sc, filelist, ci->src, ci->dst);
546        free(ci);
547    }
548
549    return 0;
550}
551
552static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
553                                  bool check_timestamps, bool list_only) {
554    copyinfo *filelist = 0;
555    copyinfo *ci, *next;
556    int pushed = 0;
557    int skipped = 0;
558
559    if ((lpath[0] == 0) || (rpath[0] == 0)) return false;
560    if (lpath[strlen(lpath) - 1] != '/') {
561        int  tmplen = strlen(lpath)+2;
562        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
563        if(tmp == 0) return false;
564        snprintf(tmp, tmplen, "%s/",lpath);
565        lpath = tmp;
566    }
567    if (rpath[strlen(rpath) - 1] != '/') {
568        int tmplen = strlen(rpath)+2;
569        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
570        if(tmp == 0) return false;
571        snprintf(tmp, tmplen, "%s/",rpath);
572        rpath = tmp;
573    }
574
575    if (local_build_list(sc, &filelist, lpath, rpath)) {
576        return false;
577    }
578
579    if (check_timestamps) {
580        for (ci = filelist; ci != 0; ci = ci->next) {
581            if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
582        }
583        for(ci = filelist; ci != 0; ci = ci->next) {
584            unsigned int timestamp, mode, size;
585            if (!sync_finish_stat(sc, &timestamp, &mode, &size)) return false;
586            if (size == ci->size) {
587                /* for links, we cannot update the atime/mtime */
588                if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
589                        (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) {
590                    ci->flag = 1;
591                }
592            }
593        }
594    }
595    for (ci = filelist; ci != 0; ci = next) {
596        next = ci->next;
597        if (ci->flag == 0) {
598            if (list_only) {
599                fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst);
600            } else {
601                if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) {
602                  return false;
603                }
604            }
605            pushed++;
606        } else {
607            skipped++;
608        }
609        free(ci);
610    }
611
612    sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
613                                         rpath,
614                                         pushed, (pushed == 1) ? "" : "s",
615                                         skipped, (skipped == 1) ? "" : "s",
616                                         sc.TransferRate().c_str()));
617    return true;
618}
619
620bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
621    SyncConnection sc;
622    if (!sc.IsValid()) return false;
623
624    bool success = true;
625    unsigned mode;
626    if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
627    bool dst_isdir = mode != 0 && S_ISDIR(mode);
628
629    if (!dst_isdir) {
630        if (srcs.size() > 1) {
631            sc.Error("target '%s' is not a directory", dst);
632            return false;
633        } else {
634            size_t dst_len = strlen(dst);
635            if (dst[dst_len - 1] == '/') {
636                sc.Error("failed to access '%s': Not a directory", dst);
637                return false;
638            }
639        }
640    }
641
642    for (const char* src_path : srcs) {
643        const char* dst_path = dst;
644        struct stat st;
645        if (stat(src_path, &st)) {
646            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
647            success = false;
648            continue;
649        }
650
651        if (S_ISDIR(st.st_mode)) {
652            success &= copy_local_dir_remote(sc, src_path, dst, false, false);
653            continue;
654        }
655
656        std::string path_holder;
657        if (mode != 0 && S_ISDIR(mode)) {
658            // If we're copying a local file to a remote directory,
659            // we really want to copy to remote_dir + "/" + local_filename.
660            path_holder = android::base::StringPrintf(
661                "%s/%s", dst_path, adb_basename(src_path).c_str());
662            dst_path = path_holder.c_str();
663        }
664        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
665    }
666
667    sc.Print("\n");
668    return success;
669}
670
671struct sync_ls_build_list_cb_args {
672    SyncConnection* sc;
673    copyinfo** filelist;
674    copyinfo** dirlist;
675    const char* rpath;
676    const char* lpath;
677};
678
679static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
680                                  const char* name, void* cookie)
681{
682    sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie);
683    copyinfo *ci;
684
685    if (S_ISDIR(mode)) {
686        copyinfo **dirlist = args->dirlist;
687
688        // Don't try recursing down "." or "..".
689        if (IsDotOrDotDot(name)) return;
690
691        ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
692        ci->next = *dirlist;
693        *dirlist = ci;
694    } else if (S_ISREG(mode) || S_ISLNK(mode)) {
695        copyinfo **filelist = args->filelist;
696
697        ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
698        ci->time = time;
699        ci->mode = mode;
700        ci->size = size;
701        ci->next = *filelist;
702        *filelist = ci;
703    } else {
704        args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name));
705    }
706}
707
708static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
709                              const char *rpath, const char *lpath) {
710    copyinfo* dirlist = nullptr;
711
712    sync_ls_build_list_cb_args args;
713    args.sc = &sc;
714    args.filelist = filelist;
715    args.dirlist = &dirlist;
716    args.rpath = rpath;
717    args.lpath = lpath;
718
719    // Put the files/dirs in rpath on the lists.
720    if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) {
721        return false;
722    }
723
724    // Recurse into each directory we found.
725    while (dirlist != NULL) {
726        copyinfo* next = dirlist->next;
727        if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
728            return false;
729        }
730        free(dirlist);
731        dirlist = next;
732    }
733
734    return true;
735}
736
737static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
738{
739    struct utimbuf times = { time, time };
740    int r1 = utime(lpath, &times);
741
742    /* use umask for permissions */
743    mode_t mask=umask(0000);
744    umask(mask);
745    int r2 = chmod(lpath, mode & ~mask);
746
747    return r1 ? : r2;
748}
749
750static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
751                                  bool copy_attrs) {
752    // Make sure that both directory paths end in a slash.
753    std::string rpath_clean(rpath);
754    std::string lpath_clean(lpath);
755    if (rpath_clean.empty() || lpath_clean.empty()) return false;
756    if (rpath_clean.back() != '/') rpath_clean.push_back('/');
757    if (lpath_clean.back() != '/') lpath_clean.push_back('/');
758
759    // Recursively build the list of files to copy.
760    sc.Print("pull: building file list...");
761    copyinfo* filelist = nullptr;
762    if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
763
764    int pulled = 0;
765    int skipped = 0;
766    copyinfo* ci = filelist;
767    while (ci) {
768        copyinfo* next = ci->next;
769        if (ci->flag == 0) {
770            sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
771            if (!sync_recv(sc, ci->src, ci->dst)) {
772                return false;
773            }
774
775            if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
776                return false;
777            }
778            pulled++;
779        } else {
780            skipped++;
781        }
782        free(ci);
783        ci = next;
784    }
785
786    sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
787                                         rpath,
788                                         pulled, (pulled == 1) ? "" : "s",
789                                         skipped, (skipped == 1) ? "" : "s",
790                                         sc.TransferRate().c_str()));
791    return true;
792}
793
794bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
795                  bool copy_attrs) {
796    SyncConnection sc;
797    if (!sc.IsValid()) return false;
798
799    bool success = true;
800    unsigned mode, time;
801    struct stat st;
802    if (stat(dst, &st)) {
803        // If we're only pulling one file, the destination path might point to
804        // a path that doesn't exist yet.
805        if (srcs.size() != 1 || errno != ENOENT) {
806            sc.Error("cannot stat '%s': %s", dst, strerror(errno));
807            return false;
808        }
809    }
810
811    bool dst_isdir = S_ISDIR(st.st_mode);
812    if (!dst_isdir) {
813        if (srcs.size() > 1) {
814            sc.Error("target '%s' is not a directory", dst);
815            return false;
816        } else {
817            size_t dst_len = strlen(dst);
818            if (dst[dst_len - 1] == '/') {
819                sc.Error("failed to access '%s': Not a directory", dst);
820                return false;
821            }
822        }
823    }
824
825    for (const char* src_path : srcs) {
826        const char* dst_path = dst;
827        if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
828        if (mode == 0) {
829            sc.Error("remote object '%s' does not exist", src_path);
830            success = false;
831            continue;
832        }
833
834        if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
835            std::string path_holder;
836            struct stat st;
837            if (stat(dst_path, &st) == 0) {
838                if (S_ISDIR(st.st_mode)) {
839                    // If we're copying a remote file to a local directory,
840                    // we really want to copy to local_dir + "/" +
841                    // basename(remote).
842                    path_holder = android::base::StringPrintf(
843                        "%s/%s", dst_path, adb_basename(src_path).c_str());
844                    dst_path = path_holder.c_str();
845                }
846            }
847            if (!sync_recv(sc, src_path, dst_path)) {
848                success = false;
849                continue;
850            } else {
851                if (copy_attrs && set_time_and_mode(dst_path, time, mode)) {
852                    success = false;
853                    continue;
854                }
855            }
856        } else if (S_ISDIR(mode)) {
857            success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
858            continue;
859        } else {
860            sc.Error("remote object '%s' not a file or directory", src_path);
861            success = false;
862            continue;
863        }
864    }
865
866    sc.Print("\n");
867    return success;
868}
869
870bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
871    SyncConnection sc;
872    if (!sc.IsValid()) return false;
873
874    return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only);
875}
876