Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06b0d6b0 authored by Dan Albert's avatar Dan Albert
Browse files

Add --sync support to push.

Passing --sync only copies files that are older on the device.

Test: nose2
Bug: None
Change-Id: I2ff6c3d1fe29262c8ee50db316aa92fc38dd7147
parent def4aae2
Loading
Loading
Loading
Loading
+13 −8
Original line number Diff line number Diff line
@@ -126,8 +126,9 @@ static void help() {
        " reverse --remove-all     remove all reverse socket connections from device\n"
        "\n"
        "file transfer:\n"
        " push LOCAL... REMOTE\n"
        " push [--sync] LOCAL... REMOTE\n"
        "     copy local files/directories to device\n"
        "     --sync: only push files that are newer on the host than the device\n"
        " pull [-a] REMOTE... LOCAL\n"
        "     copy files/dirs from device\n"
        "     -a: preserve file timestamp and mode\n"
@@ -1233,9 +1234,8 @@ static int restore(int argc, const char** argv) {
    return 0;
}

static void parse_push_pull_args(const char** arg, int narg,
                                 std::vector<const char*>* srcs,
                                 const char** dst, bool* copy_attrs) {
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
                                 const char** dst, bool* copy_attrs, bool* sync) {
    *copy_attrs = false;

    srcs->clear();
@@ -1248,6 +1248,10 @@ static void parse_push_pull_args(const char** arg, int narg,
                // Silently ignore for backwards compatibility.
            } else if (!strcmp(*arg, "-a")) {
                *copy_attrs = true;
            } else if (!strcmp(*arg, "--sync")) {
                if (sync != nullptr) {
                    *sync = true;
                }
            } else if (!strcmp(*arg, "--")) {
                ignore_flags = true;
            } else {
@@ -1654,19 +1658,20 @@ int adb_commandline(int argc, const char** argv) {
    }
    else if (!strcmp(argv[0], "push")) {
        bool copy_attrs = false;
        bool sync = false;
        std::vector<const char*> srcs;
        const char* dst = nullptr;

        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
        return do_sync_push(srcs, dst) ? 0 : 1;
        return do_sync_push(srcs, dst, sync) ? 0 : 1;
    }
    else if (!strcmp(argv[0], "pull")) {
        bool copy_attrs = false;
        std::vector<const char*> srcs;
        const char* dst = ".";

        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
        if (srcs.empty()) return syntax_error("pull requires an argument");
        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
    }
@@ -2086,7 +2091,7 @@ static int install_app_legacy(TransportType transport, const char* serial, int a
    std::vector<const char*> apk_file = {argv[last_apk]};
    std::string apk_dest = android::base::StringPrintf(
        where, android::base::Basename(argv[last_apk]).c_str());
    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
    result = pm_command(transport, serial, argc, argv);

+18 −7
Original line number Diff line number Diff line
@@ -674,11 +674,22 @@ static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat
    return true;
}

static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
                      unsigned mtime, mode_t mode)
{
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
                      mode_t mode, bool sync) {
    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);

    if (sync) {
        struct stat st;
        if (sync_lstat(sc, rpath, &st)) {
            // For links, we cannot update the atime/mtime.
            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
                sc.RecordFilesSkipped(1);
                return true;
            }
        }
    }

    if (S_ISLNK(mode)) {
#if !defined(_WIN32)
        char buf[PATH_MAX];
@@ -902,7 +913,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
            if (list_only) {
                sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
            } else {
                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
                    return false;
                }
            }
@@ -916,7 +927,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
    return true;
}

bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
    SyncConnection sc;
    if (!sc.IsValid()) return false;

@@ -981,7 +992,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
                dst_dir.append(android::base::Basename(src_path));
            }

            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
            continue;
        } else if (!should_push_file(st.st_mode)) {
            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1002,7 +1013,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {

        sc.NewTransfer();
        sc.SetExpectedTotalBytes(st.st_size);
        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
        sc.ReportTransferRate(src_path, TransferDirection::push);
    }

+1 −1
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@ union syncmsg {

void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
                  bool copy_attrs, const char* name=nullptr);

+38 −11
Original line number Diff line number Diff line
@@ -1130,8 +1130,18 @@ class FileOperationsTest(DeviceTest):
            if host_dir is not None:
                shutil.rmtree(host_dir)

    def verify_sync(self, device, temp_files, device_dir):
        """Verifies that a list of temp files was synced to the device."""
        # Confirm that every file on the device mirrors that on the host.
        for temp_file in temp_files:
            device_full_path = posixpath.join(
                device_dir, temp_file.base_name)
            dev_md5, _ = device.shell(
                [get_md5_prog(self.device), device_full_path])[0].split()
            self.assertEqual(temp_file.checksum, dev_md5)

    def test_sync(self):
        """Sync a randomly generated directory of files to specified device."""
        """Sync a host directory to the data partition."""

        try:
            base_dir = tempfile.mkdtemp()
@@ -1141,9 +1151,10 @@ class FileOperationsTest(DeviceTest):
            os.makedirs(full_dir_path)

            # Create 32 random files within the host mirror.
            temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
            temp_files = make_random_host_files(
                in_dir=full_dir_path, num_files=32)

            # Clean up any trash on the device.
            # Clean up any stale files on the device.
            device = adb.get_device()  # pylint: disable=no-member
            device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])

@@ -1155,19 +1166,35 @@ class FileOperationsTest(DeviceTest):
            else:
                os.environ['ANDROID_PRODUCT_OUT'] = old_product_out

            # Confirm that every file on the device mirrors that on the host.
            for temp_file in temp_files:
                device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
                                                  temp_file.base_name)
                dev_md5, _ = device.shell(
                    [get_md5_prog(self.device), device_full_path])[0].split()
                self.assertEqual(temp_file.checksum, dev_md5)
            self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)

            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
            #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
        finally:
            if base_dir is not None:
                shutil.rmtree(base_dir)

    def test_push_sync(self):
        """Sync a host directory to a specific path."""

        try:
            temp_dir = tempfile.mkdtemp()
            temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)

            device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')

            # Clean up any stale files on the device.
            device = adb.get_device()  # pylint: disable=no-member
            device.shell(['rm', '-rf', device_dir])

            device.push(temp_dir, device_dir, sync=True)

            self.verify_sync(device, temp_files, device_dir)

            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
        finally:
            if temp_dir is not None:
                shutil.rmtree(temp_dir)

    def test_unicode_paths(self):
        """Ensure that we can support non-ASCII paths, even on Windows."""
        name = u'로보카 폴리'