The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 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 | |
Dan Albert | b302d12 | 2015-02-24 15:51:19 -0800 | [diff] [blame] | 17 | #include <dirent.h> |
Spencer Low | 803451e | 2015-05-13 00:02:55 -0700 | [diff] [blame] | 18 | #include <inttypes.h> |
Dan Albert | b302d12 | 2015-02-24 15:51:19 -0800 | [diff] [blame] | 19 | #include <limits.h> |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 22 | #include <sys/stat.h> |
| 23 | #include <sys/time.h> |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 24 | #include <sys/types.h> |
Dan Albert | b302d12 | 2015-02-24 15:51:19 -0800 | [diff] [blame] | 25 | #include <time.h> |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 26 | #include <unistd.h> |
Greg Hackmann | 8b68914 | 2014-05-06 08:48:18 -0700 | [diff] [blame] | 27 | #include <utime.h> |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 28 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 29 | #include <chrono> |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 30 | #include <functional> |
Elliott Hughes | 4e7848d | 2015-08-24 14:49:43 -0700 | [diff] [blame] | 31 | #include <memory> |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 32 | #include <sstream> |
| 33 | #include <string> |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 34 | #include <vector> |
Elliott Hughes | 4e7848d | 2015-08-24 14:49:43 -0700 | [diff] [blame] | 35 | |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 36 | #include "sysdeps.h" |
Dan Albert | b302d12 | 2015-02-24 15:51:19 -0800 | [diff] [blame] | 37 | |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 38 | #include "adb.h" |
| 39 | #include "adb_client.h" |
Dan Albert | 66a91b0 | 2015-02-24 21:26:58 -0800 | [diff] [blame] | 40 | #include "adb_io.h" |
Alex Vallée | e916315 | 2015-05-06 17:22:25 -0400 | [diff] [blame] | 41 | #include "adb_utils.h" |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 42 | #include "file_sync_service.h" |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 43 | #include "line_printer.h" |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 44 | #include "sysdeps/errno.h" |
Josh Gao | 8998a8d | 2016-11-18 15:17:07 -0800 | [diff] [blame] | 45 | #include "sysdeps/stat.h" |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 46 | |
Elliott Hughes | f55ead9 | 2015-12-04 22:00:26 -0800 | [diff] [blame] | 47 | #include <android-base/file.h> |
| 48 | #include <android-base/strings.h> |
| 49 | #include <android-base/stringprintf.h> |
Elliott Hughes | d189cfb | 2015-07-30 17:42:01 -0700 | [diff] [blame] | 50 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 51 | struct syncsendbuf { |
| 52 | unsigned id; |
| 53 | unsigned size; |
| 54 | char data[SYNC_DATA_MAX]; |
| 55 | }; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 56 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 57 | static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) { |
| 58 | if (!adb_is_separator(local_path.back())) { |
| 59 | local_path.push_back(OS_PATH_SEPARATOR); |
| 60 | } |
| 61 | if (remote_path.back() != '/') { |
| 62 | remote_path.push_back('/'); |
| 63 | } |
| 64 | } |
| 65 | |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 66 | static bool should_pull_file(mode_t mode) { |
Josh Gao | 8998a8d | 2016-11-18 15:17:07 -0800 | [diff] [blame] | 67 | return S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode); |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | static bool should_push_file(mode_t mode) { |
Josh Gao | 8998a8d | 2016-11-18 15:17:07 -0800 | [diff] [blame] | 71 | return S_ISREG(mode) || S_ISLNK(mode); |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 72 | } |
| 73 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 74 | struct copyinfo { |
| 75 | std::string lpath; |
| 76 | std::string rpath; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 77 | int64_t time = 0; |
| 78 | uint32_t mode; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 79 | uint64_t size = 0; |
| 80 | bool skip = false; |
| 81 | |
| 82 | copyinfo(const std::string& local_path, |
| 83 | const std::string& remote_path, |
| 84 | const std::string& name, |
| 85 | unsigned int mode) |
| 86 | : lpath(local_path), rpath(remote_path), mode(mode) { |
| 87 | ensure_trailing_separators(lpath, rpath); |
| 88 | lpath.append(name); |
| 89 | rpath.append(name); |
| 90 | if (S_ISDIR(mode)) { |
| 91 | ensure_trailing_separators(lpath, rpath); |
| 92 | } |
| 93 | } |
| 94 | }; |
| 95 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 96 | enum class TransferDirection { |
| 97 | push, |
| 98 | pull, |
| 99 | }; |
| 100 | |
| 101 | struct TransferLedger { |
| 102 | std::chrono::steady_clock::time_point start_time; |
| 103 | uint64_t files_transferred; |
| 104 | uint64_t files_skipped; |
| 105 | uint64_t bytes_transferred; |
| 106 | uint64_t bytes_expected; |
| 107 | bool expect_multiple_files; |
| 108 | |
| 109 | TransferLedger() { |
| 110 | Reset(); |
| 111 | } |
| 112 | |
| 113 | bool operator==(const TransferLedger& other) const { |
| 114 | return files_transferred == other.files_transferred && |
| 115 | files_skipped == other.files_skipped && bytes_transferred == other.bytes_transferred; |
| 116 | } |
| 117 | |
| 118 | bool operator!=(const TransferLedger& other) const { |
| 119 | return !(*this == other); |
| 120 | } |
| 121 | |
| 122 | void Reset() { |
| 123 | start_time = std::chrono::steady_clock::now(); |
| 124 | files_transferred = 0; |
| 125 | files_skipped = 0; |
| 126 | bytes_transferred = 0; |
| 127 | bytes_expected = 0; |
| 128 | } |
| 129 | |
| 130 | std::string TransferRate() { |
| 131 | if (bytes_transferred == 0) return ""; |
| 132 | |
| 133 | std::chrono::duration<double> duration; |
| 134 | duration = std::chrono::steady_clock::now() - start_time; |
| 135 | |
| 136 | double s = duration.count(); |
| 137 | if (s == 0) { |
| 138 | return ""; |
| 139 | } |
| 140 | double rate = (static_cast<double>(bytes_transferred) / s) / (1024 * 1024); |
| 141 | return android::base::StringPrintf(" %.1f MB/s (%" PRIu64 " bytes in %.3fs)", rate, |
| 142 | bytes_transferred, s); |
| 143 | } |
| 144 | |
| 145 | void ReportProgress(LinePrinter& lp, const std::string& file, uint64_t file_copied_bytes, |
| 146 | uint64_t file_total_bytes) { |
| 147 | char overall_percentage_str[5] = "?"; |
Josh Gao | 4459c13 | 2016-11-17 16:02:36 -0800 | [diff] [blame] | 148 | if (bytes_expected != 0 && bytes_transferred <= bytes_expected) { |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 149 | int overall_percentage = static_cast<int>(bytes_transferred * 100 / bytes_expected); |
| 150 | // If we're pulling symbolic links, we'll pull the target of the link rather than |
| 151 | // just create a local link, and that will cause us to go over 100%. |
| 152 | if (overall_percentage <= 100) { |
| 153 | snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%", |
| 154 | overall_percentage); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | std::string output; |
| 159 | if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) { |
| 160 | // This case can happen if we're racing against something that wrote to the file |
| 161 | // between our stat and our read, or if we're reading a magic file that lies about |
| 162 | // its size. Just show how much we've copied. |
| 163 | output = android::base::StringPrintf("[%4s] %s: %" PRId64 "/?", overall_percentage_str, |
| 164 | file.c_str(), file_copied_bytes); |
| 165 | } else { |
| 166 | // If we're transferring multiple files, we want to know how far through the current |
| 167 | // file we are, as well as the overall percentage. |
| 168 | if (expect_multiple_files) { |
| 169 | int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes); |
| 170 | output = android::base::StringPrintf("[%4s] %s: %d%%", overall_percentage_str, |
| 171 | file.c_str(), file_percentage); |
| 172 | } else { |
| 173 | output = |
| 174 | android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str()); |
| 175 | } |
| 176 | } |
| 177 | lp.Print(output, LinePrinter::LineType::INFO); |
| 178 | } |
| 179 | |
| 180 | void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) { |
| 181 | const char* direction_str = (direction == TransferDirection::push) ? "pushed" : "pulled"; |
| 182 | std::stringstream ss; |
| 183 | if (!name.empty()) { |
| 184 | ss << name << ": "; |
| 185 | } |
| 186 | ss << files_transferred << " file" << ((files_transferred == 1) ? "" : "s") << " " |
| 187 | << direction_str << "."; |
| 188 | if (files_skipped > 0) { |
| 189 | ss << " " << files_skipped << " file" << ((files_skipped == 1) ? "" : "s") |
| 190 | << " skipped."; |
| 191 | } |
| 192 | ss << TransferRate(); |
| 193 | |
| 194 | lp.Print(ss.str(), LinePrinter::LineType::INFO); |
| 195 | lp.KeepInfoLine(); |
| 196 | } |
| 197 | }; |
| 198 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 199 | class SyncConnection { |
| 200 | public: |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 201 | SyncConnection() : expect_done_(false) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 202 | max = SYNC_DATA_MAX; // TODO: decide at runtime. |
| 203 | |
| 204 | std::string error; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 205 | FeatureSet features; |
| 206 | if (!adb_get_feature_set(&features, &error)) { |
| 207 | fd = -1; |
| 208 | Error("failed to get feature set: %s", error.c_str()); |
| 209 | } else { |
| 210 | have_stat_v2_ = CanUseFeature(features, kFeatureStat2); |
| 211 | fd = adb_connect("sync:", &error); |
| 212 | if (fd < 0) { |
| 213 | Error("connect failed: %s", error.c_str()); |
| 214 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 215 | } |
| 216 | } |
| 217 | |
| 218 | ~SyncConnection() { |
| 219 | if (!IsValid()) return; |
| 220 | |
Spencer Low | cc4a4b1 | 2015-10-14 17:32:44 -0700 | [diff] [blame] | 221 | if (SendQuit()) { |
| 222 | // We sent a quit command, so the server should be doing orderly |
| 223 | // shutdown soon. But if we encountered an error while we were using |
| 224 | // the connection, the server might still be sending data (before |
| 225 | // doing orderly shutdown), in which case we won't wait for all of |
| 226 | // the data nor the coming orderly shutdown. In the common success |
| 227 | // case, this will wait for the server to do orderly shutdown. |
| 228 | ReadOrderlyShutdown(fd); |
| 229 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 230 | adb_close(fd); |
Elliott Hughes | 78a37a5 | 2015-12-08 16:01:15 -0800 | [diff] [blame] | 231 | |
| 232 | line_printer_.KeepInfoLine(); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 233 | } |
| 234 | |
| 235 | bool IsValid() { return fd >= 0; } |
| 236 | |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 237 | bool ReceivedError(const char* from, const char* to) { |
| 238 | adb_pollfd pfd = {.fd = fd, .events = POLLIN}; |
| 239 | int rc = adb_poll(&pfd, 1, 0); |
| 240 | if (rc < 0) { |
| 241 | Error("failed to poll: %s", strerror(errno)); |
| 242 | return true; |
| 243 | } |
| 244 | return rc != 0; |
| 245 | } |
| 246 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 247 | void NewTransfer() { |
| 248 | current_ledger_.Reset(); |
| 249 | } |
| 250 | |
| 251 | void RecordBytesTransferred(size_t bytes) { |
| 252 | current_ledger_.bytes_transferred += bytes; |
| 253 | global_ledger_.bytes_transferred += bytes; |
| 254 | } |
| 255 | |
| 256 | void RecordFilesTransferred(size_t files) { |
| 257 | current_ledger_.files_transferred += files; |
| 258 | global_ledger_.files_transferred += files; |
| 259 | } |
| 260 | |
| 261 | void RecordFilesSkipped(size_t files) { |
| 262 | current_ledger_.files_skipped += files; |
| 263 | global_ledger_.files_skipped += files; |
| 264 | } |
| 265 | |
| 266 | void ReportProgress(const std::string& file, uint64_t file_copied_bytes, |
| 267 | uint64_t file_total_bytes) { |
| 268 | current_ledger_.ReportProgress(line_printer_, file, file_copied_bytes, file_total_bytes); |
| 269 | } |
| 270 | |
| 271 | void ReportTransferRate(const std::string& file, TransferDirection direction) { |
| 272 | current_ledger_.ReportTransferRate(line_printer_, file, direction); |
| 273 | } |
| 274 | |
| 275 | void ReportOverallTransferRate(TransferDirection direction) { |
| 276 | if (current_ledger_ != global_ledger_) { |
| 277 | global_ledger_.ReportTransferRate(line_printer_, "", direction); |
| 278 | } |
| 279 | } |
| 280 | |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 281 | bool SendRequest(int id, const char* path_and_mode) { |
| 282 | size_t path_length = strlen(path_and_mode); |
| 283 | if (path_length > 1024) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 284 | Error("SendRequest failed: path too long: %zu", path_length); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 285 | errno = ENAMETOOLONG; |
| 286 | return false; |
| 287 | } |
| 288 | |
| 289 | // Sending header and payload in a single write makes a noticeable |
| 290 | // difference to "adb sync" performance. |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 291 | std::vector<char> buf(sizeof(SyncRequest) + path_length); |
| 292 | SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 293 | req->id = id; |
| 294 | req->path_length = path_length; |
| 295 | char* data = reinterpret_cast<char*>(req + 1); |
| 296 | memcpy(data, path_and_mode, path_length); |
| 297 | |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 298 | return WriteFdExactly(fd, &buf[0], buf.size()); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 299 | } |
| 300 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 301 | bool SendStat(const char* path_and_mode) { |
| 302 | if (!have_stat_v2_) { |
| 303 | errno = ENOTSUP; |
| 304 | return false; |
| 305 | } |
| 306 | return SendRequest(ID_STAT_V2, path_and_mode); |
| 307 | } |
| 308 | |
| 309 | bool SendLstat(const char* path_and_mode) { |
| 310 | if (have_stat_v2_) { |
| 311 | return SendRequest(ID_LSTAT_V2, path_and_mode); |
| 312 | } else { |
| 313 | return SendRequest(ID_LSTAT_V1, path_and_mode); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | bool FinishStat(struct stat* st) { |
| 318 | syncmsg msg; |
| 319 | |
| 320 | memset(st, 0, sizeof(*st)); |
| 321 | if (have_stat_v2_) { |
| 322 | if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) { |
| 323 | fatal_errno("protocol fault: failed to read stat response"); |
| 324 | } |
| 325 | |
| 326 | if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) { |
| 327 | fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32, |
| 328 | msg.stat_v2.id); |
| 329 | } |
| 330 | |
| 331 | if (msg.stat_v2.error != 0) { |
| 332 | errno = errno_from_wire(msg.stat_v2.error); |
| 333 | return false; |
| 334 | } |
| 335 | |
| 336 | st->st_dev = msg.stat_v2.dev; |
| 337 | st->st_ino = msg.stat_v2.ino; |
| 338 | st->st_mode = msg.stat_v2.mode; |
| 339 | st->st_nlink = msg.stat_v2.nlink; |
| 340 | st->st_uid = msg.stat_v2.uid; |
| 341 | st->st_gid = msg.stat_v2.gid; |
| 342 | st->st_size = msg.stat_v2.size; |
| 343 | st->st_atime = msg.stat_v2.atime; |
| 344 | st->st_mtime = msg.stat_v2.mtime; |
| 345 | st->st_ctime = msg.stat_v2.ctime; |
| 346 | return true; |
| 347 | } else { |
| 348 | if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) { |
| 349 | fatal_errno("protocol fault: failed to read stat response"); |
| 350 | } |
| 351 | |
| 352 | if (msg.stat_v1.id != ID_LSTAT_V1) { |
| 353 | fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32, |
| 354 | msg.stat_v1.id); |
| 355 | } |
| 356 | |
| 357 | if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) { |
| 358 | // There's no way for us to know what the error was. |
| 359 | errno = ENOPROTOOPT; |
| 360 | return false; |
| 361 | } |
| 362 | |
| 363 | st->st_mode = msg.stat_v1.mode; |
| 364 | st->st_size = msg.stat_v1.size; |
| 365 | st->st_ctime = msg.stat_v1.time; |
| 366 | st->st_mtime = msg.stat_v1.time; |
| 367 | } |
| 368 | |
| 369 | return true; |
| 370 | } |
| 371 | |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 372 | // Sending header, payload, and footer in a single write makes a huge |
| 373 | // difference to "adb sync" performance. |
| 374 | bool SendSmallFile(const char* path_and_mode, |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 375 | const char* lpath, const char* rpath, |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 376 | unsigned mtime, |
| 377 | const char* data, size_t data_length) { |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 378 | size_t path_length = strlen(path_and_mode); |
| 379 | if (path_length > 1024) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 380 | Error("SendSmallFile failed: path too long: %zu", path_length); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 381 | errno = ENAMETOOLONG; |
| 382 | return false; |
| 383 | } |
| 384 | |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 385 | std::vector<char> buf(sizeof(SyncRequest) + path_length + |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 386 | sizeof(SyncRequest) + data_length + |
| 387 | sizeof(SyncRequest)); |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 388 | char* p = &buf[0]; |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 389 | |
| 390 | SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p); |
| 391 | req_send->id = ID_SEND; |
| 392 | req_send->path_length = path_length; |
| 393 | p += sizeof(SyncRequest); |
| 394 | memcpy(p, path_and_mode, path_length); |
| 395 | p += path_length; |
| 396 | |
| 397 | SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p); |
| 398 | req_data->id = ID_DATA; |
| 399 | req_data->path_length = data_length; |
| 400 | p += sizeof(SyncRequest); |
| 401 | memcpy(p, data, data_length); |
| 402 | p += data_length; |
| 403 | |
| 404 | SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p); |
| 405 | req_done->id = ID_DONE; |
| 406 | req_done->path_length = mtime; |
| 407 | p += sizeof(SyncRequest); |
| 408 | |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 409 | WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0])); |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 410 | expect_done_ = true; |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 411 | |
| 412 | // RecordFilesTransferred gets called in CopyDone. |
| 413 | RecordBytesTransferred(data_length); |
Josh Gao | 37e8ab4 | 2016-03-01 11:46:02 -0800 | [diff] [blame] | 414 | ReportProgress(rpath, data_length, data_length); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 415 | return true; |
| 416 | } |
| 417 | |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 418 | bool SendLargeFile(const char* path_and_mode, |
| 419 | const char* lpath, const char* rpath, |
| 420 | unsigned mtime) { |
| 421 | if (!SendRequest(ID_SEND, path_and_mode)) { |
| 422 | Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno)); |
| 423 | return false; |
| 424 | } |
| 425 | |
| 426 | struct stat st; |
| 427 | if (stat(lpath, &st) == -1) { |
| 428 | Error("cannot stat '%s': %s", lpath, strerror(errno)); |
| 429 | return false; |
| 430 | } |
| 431 | |
| 432 | uint64_t total_size = st.st_size; |
| 433 | uint64_t bytes_copied = 0; |
| 434 | |
| 435 | int lfd = adb_open(lpath, O_RDONLY); |
| 436 | if (lfd < 0) { |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 437 | Error("opening '%s' locally failed: %s", lpath, strerror(errno)); |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 438 | return false; |
| 439 | } |
| 440 | |
| 441 | syncsendbuf sbuf; |
| 442 | sbuf.id = ID_DATA; |
| 443 | while (true) { |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 444 | int bytes_read = adb_read(lfd, sbuf.data, max); |
| 445 | if (bytes_read == -1) { |
| 446 | Error("reading '%s' locally failed: %s", lpath, strerror(errno)); |
| 447 | adb_close(lfd); |
| 448 | return false; |
| 449 | } else if (bytes_read == 0) { |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 450 | break; |
| 451 | } |
| 452 | |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 453 | sbuf.size = bytes_read; |
| 454 | WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read); |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 455 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 456 | RecordBytesTransferred(bytes_read); |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 457 | bytes_copied += bytes_read; |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 458 | |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 459 | // Check to see if we've received an error from the other side. |
| 460 | if (ReceivedError(lpath, rpath)) { |
| 461 | break; |
| 462 | } |
| 463 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 464 | ReportProgress(rpath, bytes_copied, total_size); |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | adb_close(lfd); |
| 468 | |
| 469 | syncmsg msg; |
| 470 | msg.data.id = ID_DONE; |
| 471 | msg.data.size = mtime; |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 472 | expect_done_ = true; |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 473 | |
| 474 | // RecordFilesTransferred gets called in CopyDone. |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 475 | return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data)); |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 476 | } |
| 477 | |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 478 | bool CopyDone(const char* from, const char* to) { |
| 479 | syncmsg msg; |
| 480 | if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) { |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 481 | Error("failed to copy '%s' to '%s': couldn't read from device", from, to); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 482 | return false; |
| 483 | } |
| 484 | if (msg.status.id == ID_OKAY) { |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 485 | if (expect_done_) { |
| 486 | expect_done_ = false; |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 487 | RecordFilesTransferred(1); |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 488 | return true; |
| 489 | } else { |
| 490 | Error("failed to copy '%s' to '%s': received premature success", from, to); |
| 491 | return true; |
| 492 | } |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 493 | } |
| 494 | if (msg.status.id != ID_FAIL) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 495 | Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 496 | return false; |
| 497 | } |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 498 | return ReportCopyFailure(from, to, msg); |
| 499 | } |
| 500 | |
| 501 | bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) { |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 502 | std::vector<char> buf(msg.status.msglen + 1); |
| 503 | if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 504 | Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", |
| 505 | from, to, strerror(errno)); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 506 | return false; |
| 507 | } |
Elliott Hughes | 6c73bfc | 2015-10-27 13:40:35 -0700 | [diff] [blame] | 508 | buf[msg.status.msglen] = 0; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 509 | Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 510 | return false; |
| 511 | } |
| 512 | |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 513 | |
Josh Gao | cbf485f | 2015-11-02 17:15:57 -0800 | [diff] [blame] | 514 | void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { |
| 515 | std::string s; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 516 | |
Josh Gao | cbf485f | 2015-11-02 17:15:57 -0800 | [diff] [blame] | 517 | va_list ap; |
| 518 | va_start(ap, fmt); |
| 519 | android::base::StringAppendV(&s, fmt, ap); |
| 520 | va_end(ap); |
| 521 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 522 | line_printer_.Print(s, LinePrinter::INFO); |
Josh Gao | cbf485f | 2015-11-02 17:15:57 -0800 | [diff] [blame] | 523 | } |
| 524 | |
Josh Gao | f35fede | 2016-08-04 11:43:00 -0700 | [diff] [blame] | 525 | void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { |
| 526 | std::string s; |
| 527 | |
| 528 | va_list ap; |
| 529 | va_start(ap, fmt); |
| 530 | android::base::StringAppendV(&s, fmt, ap); |
| 531 | va_end(ap); |
| 532 | |
| 533 | line_printer_.Print(s, LinePrinter::INFO); |
| 534 | line_printer_.KeepInfoLine(); |
| 535 | } |
| 536 | |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 537 | void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { |
| 538 | std::string s = "adb: error: "; |
| 539 | |
| 540 | va_list ap; |
| 541 | va_start(ap, fmt); |
| 542 | android::base::StringAppendV(&s, fmt, ap); |
| 543 | va_end(ap); |
| 544 | |
Elliott Hughes | 78a37a5 | 2015-12-08 16:01:15 -0800 | [diff] [blame] | 545 | line_printer_.Print(s, LinePrinter::ERROR); |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 546 | } |
| 547 | |
Josh Gao | a544cc6 | 2015-11-07 17:18:44 -0800 | [diff] [blame] | 548 | void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { |
| 549 | std::string s = "adb: warning: "; |
| 550 | |
| 551 | va_list ap; |
| 552 | va_start(ap, fmt); |
| 553 | android::base::StringAppendV(&s, fmt, ap); |
| 554 | va_end(ap); |
| 555 | |
Elliott Hughes | 78a37a5 | 2015-12-08 16:01:15 -0800 | [diff] [blame] | 556 | line_printer_.Print(s, LinePrinter::WARNING); |
Josh Gao | a544cc6 | 2015-11-07 17:18:44 -0800 | [diff] [blame] | 557 | } |
| 558 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 559 | void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) { |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 560 | current_ledger_.bytes_expected = 0; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 561 | for (const copyinfo& ci : file_list) { |
| 562 | // Unfortunately, this doesn't work for symbolic links, because we'll copy the |
| 563 | // target of the link rather than just creating a link. (But ci.size is the link size.) |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 564 | if (!ci.skip) current_ledger_.bytes_expected += ci.size; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 565 | } |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 566 | current_ledger_.expect_multiple_files = true; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | void SetExpectedTotalBytes(uint64_t expected_total_bytes) { |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 570 | current_ledger_.bytes_expected = expected_total_bytes; |
| 571 | current_ledger_.expect_multiple_files = false; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 572 | } |
| 573 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 574 | // TODO: add a char[max] buffer here, to replace syncsendbuf... |
| 575 | int fd; |
| 576 | size_t max; |
| 577 | |
| 578 | private: |
Josh Gao | 2db75ce | 2016-02-19 15:55:55 -0800 | [diff] [blame] | 579 | bool expect_done_; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 580 | bool have_stat_v2_; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 581 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 582 | TransferLedger global_ledger_; |
| 583 | TransferLedger current_ledger_; |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 584 | LinePrinter line_printer_; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 585 | |
Spencer Low | cc4a4b1 | 2015-10-14 17:32:44 -0700 | [diff] [blame] | 586 | bool SendQuit() { |
| 587 | return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse? |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 588 | } |
| 589 | |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 590 | bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) { |
| 591 | if (!WriteFdExactly(fd, data, data_length)) { |
| 592 | if (errno == ECONNRESET) { |
| 593 | // Assume adbd told us why it was closing the connection, and |
| 594 | // try to read failure reason from adbd. |
| 595 | syncmsg msg; |
| 596 | if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) { |
| 597 | Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno)); |
| 598 | } else if (msg.status.id != ID_FAIL) { |
| 599 | Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id); |
| 600 | } else { |
| 601 | ReportCopyFailure(from, to, msg); |
| 602 | } |
| 603 | } else { |
| 604 | Error("%zu-byte write failed: %s", data_length, strerror(errno)); |
| 605 | } |
| 606 | _exit(1); |
| 607 | } |
| 608 | return true; |
| 609 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 610 | }; |
| 611 | |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 612 | typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 613 | |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 614 | static bool sync_ls(SyncConnection& sc, const char* path, |
Chih-Hung Hsieh | 90b40f6 | 2016-07-27 16:25:51 -0700 | [diff] [blame] | 615 | const std::function<sync_ls_cb>& func) { |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 616 | if (!sc.SendRequest(ID_LIST, path)) return false; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 617 | |
| 618 | while (true) { |
| 619 | syncmsg msg; |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 620 | if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 621 | |
| 622 | if (msg.dent.id == ID_DONE) return true; |
| 623 | if (msg.dent.id != ID_DENT) return false; |
| 624 | |
Elliott Hughes | 9e8e355 | 2015-08-24 14:27:03 -0700 | [diff] [blame] | 625 | size_t len = msg.dent.namelen; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 626 | if (len > 256) return false; // TODO: resize buffer? continue? |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 627 | |
Elliott Hughes | d189cfb | 2015-07-30 17:42:01 -0700 | [diff] [blame] | 628 | char buf[257]; |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 629 | if (!ReadFdExactly(sc.fd, buf, len)) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 630 | buf[len] = 0; |
| 631 | |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 632 | func(msg.dent.mode, msg.dent.size, msg.dent.time, buf); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 633 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 634 | } |
| 635 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 636 | static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) { |
| 637 | return sc.SendStat(path) && sc.FinishStat(st); |
| 638 | } |
| 639 | |
| 640 | static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) { |
| 641 | return sc.SendLstat(path) && sc.FinishStat(st); |
| 642 | } |
| 643 | |
| 644 | static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) { |
| 645 | if (sync_stat(sc, path, st)) { |
| 646 | return true; |
| 647 | } |
| 648 | |
| 649 | if (errno != ENOTSUP) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 650 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 651 | } |
| 652 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 653 | // Try to emulate the parts we can when talking to older adbds. |
| 654 | bool lstat_result = sync_lstat(sc, path, st); |
| 655 | if (!lstat_result) { |
| 656 | return false; |
| 657 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 658 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 659 | if (S_ISLNK(st->st_mode)) { |
| 660 | // If the target is a symlink, figure out whether it's a file or a directory. |
| 661 | // Also, zero out the st_size field, since no one actually cares what the path length is. |
| 662 | st->st_size = 0; |
| 663 | std::string dir_path = path; |
| 664 | dir_path.push_back('/'); |
| 665 | struct stat tmp_st; |
| 666 | |
| 667 | st->st_mode &= ~S_IFMT; |
| 668 | if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) { |
| 669 | st->st_mode |= S_IFDIR; |
| 670 | } else { |
| 671 | st->st_mode |= S_IFREG; |
| 672 | } |
| 673 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 674 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 675 | } |
| 676 | |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 677 | static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime, |
| 678 | mode_t mode, bool sync) { |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 679 | std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode); |
| 680 | |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 681 | if (sync) { |
| 682 | struct stat st; |
| 683 | if (sync_lstat(sc, rpath, &st)) { |
| 684 | // For links, we cannot update the atime/mtime. |
| 685 | if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) || |
| 686 | (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) { |
| 687 | sc.RecordFilesSkipped(1); |
| 688 | return true; |
| 689 | } |
| 690 | } |
| 691 | } |
| 692 | |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 693 | if (S_ISLNK(mode)) { |
| 694 | #if !defined(_WIN32) |
| 695 | char buf[PATH_MAX]; |
| 696 | ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1); |
| 697 | if (data_length == -1) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 698 | sc.Error("readlink '%s' failed: %s", lpath, strerror(errno)); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 699 | return false; |
| 700 | } |
| 701 | buf[data_length++] = '\0'; |
| 702 | |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 703 | if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) { |
| 704 | return false; |
| 705 | } |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 706 | return sc.CopyDone(lpath, rpath); |
| 707 | #endif |
| 708 | } |
| 709 | |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 710 | struct stat st; |
| 711 | if (stat(lpath, &st) == -1) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 712 | sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno)); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 713 | return false; |
| 714 | } |
| 715 | if (st.st_size < SYNC_DATA_MAX) { |
| 716 | std::string data; |
Josh Gao | 1deea10 | 2016-09-14 16:13:50 -0700 | [diff] [blame] | 717 | if (!android::base::ReadFileToString(lpath, &data, true)) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 718 | sc.Error("failed to read all of '%s': %s", lpath, strerror(errno)); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 719 | return false; |
| 720 | } |
Elliott Hughes | 54e3efe | 2015-11-20 22:01:06 -0800 | [diff] [blame] | 721 | if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, |
| 722 | data.data(), data.size())) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 723 | return false; |
| 724 | } |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 725 | } else { |
Elliott Hughes | 7275d80 | 2015-11-20 17:35:17 -0800 | [diff] [blame] | 726 | if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 727 | return false; |
| 728 | } |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 729 | } |
| 730 | return sc.CopyDone(lpath, rpath); |
| 731 | } |
| 732 | |
Felipe Leme | b644703 | 2016-04-01 17:43:27 -0700 | [diff] [blame] | 733 | static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, |
Josh Gao | 5c3e3fb | 2016-12-06 14:07:53 -0800 | [diff] [blame] | 734 | const char* name, uint64_t expected_size) { |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 735 | if (!sc.SendRequest(ID_RECV, rpath)) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 736 | |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 737 | adb_unlink(lpath); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 738 | int lfd = adb_creat(lpath, 0644); |
| 739 | if (lfd < 0) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 740 | sc.Error("cannot create '%s': %s", lpath, strerror(errno)); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 741 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 742 | } |
| 743 | |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 744 | uint64_t bytes_copied = 0; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 745 | while (true) { |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 746 | syncmsg msg; |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 747 | if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) { |
Spencer Low | c1a3133 | 2015-08-28 01:07:30 -0700 | [diff] [blame] | 748 | adb_close(lfd); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 749 | adb_unlink(lpath); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 750 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 751 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 752 | |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 753 | if (msg.data.id == ID_DONE) break; |
| 754 | |
| 755 | if (msg.data.id != ID_DATA) { |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 756 | adb_close(lfd); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 757 | adb_unlink(lpath); |
| 758 | sc.ReportCopyFailure(rpath, lpath, msg); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 759 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 760 | } |
| 761 | |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 762 | if (msg.data.size > sc.max) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 763 | sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 764 | adb_close(lfd); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 765 | adb_unlink(lpath); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 766 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 767 | } |
| 768 | |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 769 | char buffer[SYNC_DATA_MAX]; |
| 770 | if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) { |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 771 | adb_close(lfd); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 772 | adb_unlink(lpath); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 773 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 774 | } |
| 775 | |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 776 | if (!WriteFdExactly(lfd, buffer, msg.data.size)) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 777 | sc.Error("cannot write '%s': %s", lpath, strerror(errno)); |
Elliott Hughes | e4ed32f | 2015-10-23 21:06:11 -0700 | [diff] [blame] | 778 | adb_close(lfd); |
| 779 | adb_unlink(lpath); |
| 780 | return false; |
| 781 | } |
| 782 | |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 783 | bytes_copied += msg.data.size; |
| 784 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 785 | sc.RecordBytesTransferred(msg.data.size); |
Josh Gao | 5c3e3fb | 2016-12-06 14:07:53 -0800 | [diff] [blame] | 786 | sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 787 | } |
| 788 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 789 | sc.RecordFilesTransferred(1); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 790 | adb_close(lfd); |
Elliott Hughes | dde00be | 2015-09-27 12:55:37 -0700 | [diff] [blame] | 791 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 792 | } |
| 793 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 794 | bool do_sync_ls(const char* path) { |
| 795 | SyncConnection sc; |
| 796 | if (!sc.IsValid()) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 797 | |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 798 | return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time, |
| 799 | const char* name) { |
| 800 | printf("%08x %08x %08x %s\n", mode, size, time, name); |
| 801 | }); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 802 | } |
| 803 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 804 | static bool IsDotOrDotDot(const char* name) { |
| 805 | return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); |
| 806 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 807 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 808 | static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list, |
Josh Gao | 8efa646 | 2015-11-03 15:26:38 -0800 | [diff] [blame] | 809 | const std::string& lpath, |
| 810 | const std::string& rpath) { |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 811 | std::vector<copyinfo> dirlist; |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 812 | std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 813 | if (!dir) { |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 814 | sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno)); |
Josh Gao | 8efa646 | 2015-11-03 15:26:38 -0800 | [diff] [blame] | 815 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 816 | } |
| 817 | |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 818 | bool empty_dir = true; |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 819 | dirent* de; |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 820 | while ((de = readdir(dir.get()))) { |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 821 | if (IsDotOrDotDot(de->d_name)) { |
| 822 | continue; |
| 823 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 824 | |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 825 | empty_dir = false; |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 826 | std::string stat_path = lpath + de->d_name; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 827 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 828 | struct stat st; |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 829 | if (lstat(stat_path.c_str(), &st) == -1) { |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 830 | sc.Error("cannot lstat '%s': %s", stat_path.c_str(), |
| 831 | strerror(errno)); |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 832 | continue; |
| 833 | } |
| 834 | |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 835 | copyinfo ci(lpath, rpath, de->d_name, st.st_mode); |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 836 | if (S_ISDIR(st.st_mode)) { |
| 837 | dirlist.push_back(ci); |
| 838 | } else { |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 839 | if (!should_push_file(st.st_mode)) { |
| 840 | sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode); |
Josh Gao | b3cab93 | 2015-11-30 10:21:25 -0800 | [diff] [blame] | 841 | ci.skip = true; |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 842 | } |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 843 | ci.time = st.st_mtime; |
| 844 | ci.size = st.st_size; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 845 | file_list->push_back(ci); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 846 | } |
| 847 | } |
| 848 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 849 | // Close this directory and recurse. |
| 850 | dir.reset(); |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 851 | |
| 852 | // Add the current directory to the list if it was empty, to ensure that |
| 853 | // it gets created. |
| 854 | if (empty_dir) { |
| 855 | // TODO(b/25566053): Make pushing empty directories work. |
| 856 | // TODO(b/25457350): We don't preserve permissions on directories. |
Josh Gao | a544cc6 | 2015-11-07 17:18:44 -0800 | [diff] [blame] | 857 | sc.Warning("skipping empty directory '%s'", lpath.c_str()); |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 858 | copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath), |
| 859 | android::base::Basename(lpath), S_IFDIR); |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 860 | ci.skip = true; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 861 | file_list->push_back(ci); |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 862 | return true; |
| 863 | } |
| 864 | |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 865 | for (const copyinfo& ci : dirlist) { |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 866 | local_build_list(sc, file_list, ci.lpath, ci.rpath); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 867 | } |
| 868 | |
Josh Gao | 8efa646 | 2015-11-03 15:26:38 -0800 | [diff] [blame] | 869 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 870 | } |
| 871 | |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 872 | static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, |
| 873 | std::string rpath, bool check_timestamps, |
| 874 | bool list_only) { |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 875 | sc.NewTransfer(); |
| 876 | |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 877 | // Make sure that both directory paths end in a slash. |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 878 | // Both paths are known to be nonempty, so we don't need to check. |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 879 | ensure_trailing_separators(lpath, rpath); |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 880 | |
| 881 | // Recursively build the list of files to copy. |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 882 | std::vector<copyinfo> file_list; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 883 | int skipped = 0; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 884 | if (!local_build_list(sc, &file_list, lpath, rpath)) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 885 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 886 | } |
| 887 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 888 | if (check_timestamps) { |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 889 | for (const copyinfo& ci : file_list) { |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 890 | if (!sc.SendLstat(ci.rpath.c_str())) { |
| 891 | sc.Error("failed to send lstat"); |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 892 | return false; |
| 893 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 894 | } |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 895 | for (copyinfo& ci : file_list) { |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 896 | struct stat st; |
| 897 | if (sc.FinishStat(&st)) { |
| 898 | if (st.st_size == static_cast<off_t>(ci.size)) { |
| 899 | // For links, we cannot update the atime/mtime. |
| 900 | if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) || |
| 901 | (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) { |
| 902 | ci.skip = true; |
| 903 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 904 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 905 | } |
| 906 | } |
| 907 | } |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 908 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 909 | sc.ComputeExpectedTotalBytes(file_list); |
| 910 | |
| 911 | for (const copyinfo& ci : file_list) { |
Josh Gao | cb094c6 | 2015-11-03 14:44:04 -0800 | [diff] [blame] | 912 | if (!ci.skip) { |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 913 | if (list_only) { |
Josh Gao | 23891f1 | 2016-08-04 14:55:23 -0700 | [diff] [blame] | 914 | sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str()); |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 915 | } else { |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 916 | if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) { |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 917 | return false; |
Elliott Hughes | f5cdc1d | 2015-10-27 16:03:15 -0700 | [diff] [blame] | 918 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 919 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 920 | } else { |
| 921 | skipped++; |
| 922 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 923 | } |
| 924 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 925 | sc.RecordFilesSkipped(skipped); |
| 926 | sc.ReportTransferRate(lpath, TransferDirection::push); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 927 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 928 | } |
| 929 | |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 930 | bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 931 | SyncConnection sc; |
| 932 | if (!sc.IsValid()) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 933 | |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 934 | bool success = true; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 935 | bool dst_exists; |
| 936 | bool dst_isdir; |
| 937 | |
| 938 | struct stat st; |
| 939 | if (sync_stat_fallback(sc, dst, &st)) { |
| 940 | dst_exists = true; |
| 941 | dst_isdir = S_ISDIR(st.st_mode); |
| 942 | } else { |
| 943 | if (errno == ENOENT || errno == ENOPROTOOPT) { |
| 944 | dst_exists = false; |
| 945 | dst_isdir = false; |
| 946 | } else { |
| 947 | sc.Error("stat failed when trying to push to %s: %s", dst, strerror(errno)); |
| 948 | return false; |
| 949 | } |
| 950 | } |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 951 | |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 952 | if (!dst_isdir) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 953 | if (srcs.size() > 1) { |
| 954 | sc.Error("target '%s' is not a directory", dst); |
| 955 | return false; |
| 956 | } else { |
| 957 | size_t dst_len = strlen(dst); |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 958 | |
| 959 | // A path that ends with a slash doesn't have to be a directory if |
| 960 | // it doesn't exist yet. |
| 961 | if (dst[dst_len - 1] == '/' && dst_exists) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 962 | sc.Error("failed to access '%s': Not a directory", dst); |
| 963 | return false; |
| 964 | } |
| 965 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 966 | } |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 967 | |
| 968 | for (const char* src_path : srcs) { |
| 969 | const char* dst_path = dst; |
| 970 | struct stat st; |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 971 | if (stat(src_path, &st) == -1) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 972 | sc.Error("cannot stat '%s': %s", src_path, strerror(errno)); |
| 973 | success = false; |
| 974 | continue; |
| 975 | } |
| 976 | |
| 977 | if (S_ISDIR(st.st_mode)) { |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 978 | std::string dst_dir = dst; |
| 979 | |
| 980 | // If the destination path existed originally, the source directory |
| 981 | // should be copied as a child of the destination. |
| 982 | if (dst_exists) { |
| 983 | if (!dst_isdir) { |
| 984 | sc.Error("target '%s' is not a directory", dst); |
| 985 | return false; |
| 986 | } |
| 987 | // dst is a POSIX path, so we don't want to use the sysdeps |
| 988 | // helpers here. |
| 989 | if (dst_dir.back() != '/') { |
| 990 | dst_dir.push_back('/'); |
| 991 | } |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 992 | dst_dir.append(android::base::Basename(src_path)); |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 993 | } |
| 994 | |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 995 | success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 996 | continue; |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 997 | } else if (!should_push_file(st.st_mode)) { |
| 998 | sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode); |
| 999 | continue; |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1000 | } |
| 1001 | |
| 1002 | std::string path_holder; |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 1003 | if (dst_isdir) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1004 | // If we're copying a local file to a remote directory, |
| 1005 | // we really want to copy to remote_dir + "/" + local_filename. |
Josh Gao | 2e4ed0a | 2016-02-03 14:55:24 -0800 | [diff] [blame] | 1006 | path_holder = dst_path; |
| 1007 | if (path_holder.back() != '/') { |
| 1008 | path_holder.push_back('/'); |
| 1009 | } |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 1010 | path_holder += android::base::Basename(src_path); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1011 | dst_path = path_holder.c_str(); |
| 1012 | } |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1013 | |
| 1014 | sc.NewTransfer(); |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1015 | sc.SetExpectedTotalBytes(st.st_size); |
Dan Albert | 8449e06 | 2017-05-18 22:56:48 -0700 | [diff] [blame] | 1016 | success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync); |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1017 | sc.ReportTransferRate(src_path, TransferDirection::push); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1018 | } |
| 1019 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1020 | sc.ReportOverallTransferRate(TransferDirection::push); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1021 | return success; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1022 | } |
| 1023 | |
Josh Gao | 5c0d55a | 2016-02-25 17:12:45 -0800 | [diff] [blame] | 1024 | static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list, |
| 1025 | const std::string& rpath, const std::string& lpath) { |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1026 | std::vector<copyinfo> dirlist; |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1027 | std::vector<copyinfo> linklist; |
Josh Gao | 5c0d55a | 2016-02-25 17:12:45 -0800 | [diff] [blame] | 1028 | |
| 1029 | // Add an entry for the current directory to ensure it gets created before pulling its contents. |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 1030 | copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath), |
| 1031 | android::base::Basename(lpath), S_IFDIR); |
Josh Gao | 5c0d55a | 2016-02-25 17:12:45 -0800 | [diff] [blame] | 1032 | file_list->push_back(ci); |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1033 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1034 | // Put the files/dirs in rpath on the lists. |
Josh Gao | b3cab93 | 2015-11-30 10:21:25 -0800 | [diff] [blame] | 1035 | auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) { |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1036 | if (IsDotOrDotDot(name)) { |
| 1037 | return; |
| 1038 | } |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1039 | |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1040 | copyinfo ci(lpath, rpath, name, mode); |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1041 | if (S_ISDIR(mode)) { |
| 1042 | dirlist.push_back(ci); |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1043 | } else if (S_ISLNK(mode)) { |
| 1044 | linklist.push_back(ci); |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1045 | } else { |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1046 | if (!should_pull_file(ci.mode)) { |
| 1047 | sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode); |
| 1048 | ci.skip = true; |
| 1049 | } |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1050 | ci.time = time; |
| 1051 | ci.size = size; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1052 | file_list->push_back(ci); |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1053 | } |
| 1054 | }; |
| 1055 | |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 1056 | if (!sync_ls(sc, rpath.c_str(), callback)) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1057 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1058 | } |
| 1059 | |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1060 | // Check each symlink we found to see whether it's a file or directory. |
| 1061 | for (copyinfo& link_ci : linklist) { |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1062 | struct stat st; |
| 1063 | if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) { |
| 1064 | sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno)); |
| 1065 | continue; |
| 1066 | } |
| 1067 | |
| 1068 | if (S_ISDIR(st.st_mode)) { |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1069 | dirlist.emplace_back(std::move(link_ci)); |
| 1070 | } else { |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1071 | file_list->emplace_back(std::move(link_ci)); |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1072 | } |
| 1073 | } |
| 1074 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1075 | // Recurse into each directory we found. |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1076 | while (!dirlist.empty()) { |
| 1077 | copyinfo current = dirlist.back(); |
| 1078 | dirlist.pop_back(); |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1079 | if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1080 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1081 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1082 | } |
| 1083 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1084 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1085 | } |
| 1086 | |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1087 | static int set_time_and_mode(const std::string& lpath, time_t time, |
| 1088 | unsigned int mode) { |
Greg Hackmann | 8b68914 | 2014-05-06 08:48:18 -0700 | [diff] [blame] | 1089 | struct utimbuf times = { time, time }; |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1090 | int r1 = utime(lpath.c_str(), ×); |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1091 | |
| 1092 | /* use umask for permissions */ |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1093 | mode_t mask = umask(0000); |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1094 | umask(mask); |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1095 | int r2 = chmod(lpath.c_str(), mode & ~mask); |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1096 | |
Spencer Low | 28bc2cb | 2015-11-07 18:51:54 -0800 | [diff] [blame] | 1097 | return r1 ? r1 : r2; |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1098 | } |
| 1099 | |
Josh Gao | d20cf38 | 2015-11-03 15:23:03 -0800 | [diff] [blame] | 1100 | static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, |
| 1101 | std::string lpath, bool copy_attrs) { |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1102 | sc.NewTransfer(); |
| 1103 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1104 | // Make sure that both directory paths end in a slash. |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 1105 | // Both paths are known to be nonempty, so we don't need to check. |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1106 | ensure_trailing_separators(lpath, rpath); |
Riley Andrews | c736a94 | 2014-12-12 13:12:36 -0800 | [diff] [blame] | 1107 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1108 | // Recursively build the list of files to copy. |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1109 | sc.Printf("pull: building file list..."); |
| 1110 | std::vector<copyinfo> file_list; |
| 1111 | if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) { |
Josh Gao | 204d21e | 2015-11-02 16:45:47 -0800 | [diff] [blame] | 1112 | return false; |
| 1113 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1114 | |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1115 | sc.ComputeExpectedTotalBytes(file_list); |
| 1116 | |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1117 | int skipped = 0; |
Elliott Hughes | 7b43fa5 | 2015-12-17 17:05:29 -0800 | [diff] [blame] | 1118 | for (const copyinfo &ci : file_list) { |
Josh Gao | cb094c6 | 2015-11-03 14:44:04 -0800 | [diff] [blame] | 1119 | if (!ci.skip) { |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1120 | if (S_ISDIR(ci.mode)) { |
| 1121 | // Entry is for an empty directory, create it and continue. |
| 1122 | // TODO(b/25457350): We don't preserve permissions on directories. |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1123 | if (!mkdirs(ci.lpath)) { |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1124 | sc.Error("failed to create directory '%s': %s", |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1125 | ci.lpath.c_str(), strerror(errno)); |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1126 | return false; |
| 1127 | } |
Josh Gao | a90563e | 2015-11-04 14:57:04 -0800 | [diff] [blame] | 1128 | continue; |
| 1129 | } |
| 1130 | |
Josh Gao | 5c3e3fb | 2016-12-06 14:07:53 -0800 | [diff] [blame] | 1131 | if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1132 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1133 | } |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1134 | |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1135 | if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1136 | return false; |
Lajos Molnar | 4e23e3c | 2013-04-19 12:41:09 -0700 | [diff] [blame] | 1137 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1138 | } else { |
| 1139 | skipped++; |
| 1140 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1141 | } |
| 1142 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1143 | sc.RecordFilesSkipped(skipped); |
| 1144 | sc.ReportTransferRate(rpath, TransferDirection::pull); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1145 | return true; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1146 | } |
| 1147 | |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1148 | bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, |
Felipe Leme | b644703 | 2016-04-01 17:43:27 -0700 | [diff] [blame] | 1149 | bool copy_attrs, const char* name) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1150 | SyncConnection sc; |
| 1151 | if (!sc.IsValid()) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1152 | |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1153 | bool success = true; |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1154 | struct stat st; |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 1155 | bool dst_exists = true; |
| 1156 | |
| 1157 | if (stat(dst, &st) == -1) { |
| 1158 | dst_exists = false; |
| 1159 | |
| 1160 | // If we're only pulling one path, the destination path might point to |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1161 | // a path that doesn't exist yet. |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 1162 | if (srcs.size() == 1 && errno == ENOENT) { |
| 1163 | // However, its parent must exist. |
| 1164 | struct stat parent_st; |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 1165 | if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) { |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 1166 | sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno)); |
| 1167 | return false; |
| 1168 | } |
| 1169 | } else { |
| 1170 | sc.Error("failed to access '%s': %s", dst, strerror(errno)); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1171 | return false; |
| 1172 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1173 | } |
| 1174 | |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 1175 | bool dst_isdir = dst_exists && S_ISDIR(st.st_mode); |
| 1176 | if (!dst_isdir) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1177 | if (srcs.size() > 1) { |
| 1178 | sc.Error("target '%s' is not a directory", dst); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1179 | return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1180 | } else { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1181 | size_t dst_len = strlen(dst); |
Josh Gao | a5cea71 | 2015-11-07 15:27:26 -0800 | [diff] [blame] | 1182 | |
| 1183 | // A path that ends with a slash doesn't have to be a directory if |
| 1184 | // it doesn't exist yet. |
Josh Gao | a3b6a06 | 2015-11-09 11:12:14 -0800 | [diff] [blame] | 1185 | if (adb_is_separator(dst[dst_len - 1]) && dst_exists) { |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1186 | sc.Error("failed to access '%s': Not a directory", dst); |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1187 | return false; |
Elliott Hughes | d189cfb | 2015-07-30 17:42:01 -0700 | [diff] [blame] | 1188 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1189 | } |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1190 | } |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1191 | |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1192 | for (const char* src_path : srcs) { |
| 1193 | const char* dst_path = dst; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1194 | struct stat src_st; |
| 1195 | if (!sync_stat_fallback(sc, src_path, &src_st)) { |
| 1196 | if (errno == ENOPROTOOPT) { |
| 1197 | sc.Error("remote object '%s' does not exist", src_path); |
| 1198 | } else { |
| 1199 | sc.Error("failed to stat remote object '%s': %s", src_path, strerror(errno)); |
| 1200 | } |
| 1201 | |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1202 | success = false; |
| 1203 | continue; |
| 1204 | } |
| 1205 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1206 | bool src_isdir = S_ISDIR(src_st.st_mode); |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1207 | if (src_isdir) { |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 1208 | std::string dst_dir = dst; |
| 1209 | |
| 1210 | // If the destination path existed originally, the source directory |
| 1211 | // should be copied as a child of the destination. |
| 1212 | if (dst_exists) { |
| 1213 | if (!dst_isdir) { |
| 1214 | sc.Error("target '%s' is not a directory", dst); |
| 1215 | return false; |
| 1216 | } |
| 1217 | if (!adb_is_separator(dst_dir.back())) { |
| 1218 | dst_dir.push_back(OS_PATH_SEPARATOR); |
| 1219 | } |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 1220 | dst_dir.append(android::base::Basename(src_path)); |
Josh Gao | 8acf06c | 2015-11-07 15:38:19 -0800 | [diff] [blame] | 1221 | } |
| 1222 | |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1223 | success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1224 | continue; |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1225 | } else if (!should_pull_file(src_st.st_mode)) { |
| 1226 | sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode); |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1227 | continue; |
| 1228 | } |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1229 | |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1230 | std::string path_holder; |
| 1231 | if (dst_isdir) { |
| 1232 | // If we're copying a remote file to a local directory, we |
| 1233 | // really want to copy to local_dir + OS_PATH_SEPARATOR + |
| 1234 | // basename(remote). |
| 1235 | path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR, |
Colin Cross | e679407 | 2017-02-23 21:23:05 -0800 | [diff] [blame] | 1236 | android::base::Basename(src_path).c_str()); |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1237 | dst_path = path_holder.c_str(); |
| 1238 | } |
Josh Gao | d9a2fd6 | 2015-12-09 14:03:30 -0800 | [diff] [blame] | 1239 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1240 | sc.NewTransfer(); |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1241 | sc.SetExpectedTotalBytes(src_st.st_size); |
Josh Gao | 5c3e3fb | 2016-12-06 14:07:53 -0800 | [diff] [blame] | 1242 | if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) { |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1243 | success = false; |
| 1244 | continue; |
| 1245 | } |
| 1246 | |
Josh Gao | a2cf375 | 2016-12-05 17:11:34 -0800 | [diff] [blame] | 1247 | if (copy_attrs && set_time_and_mode(dst_path, src_st.st_mtime, src_st.st_mode) != 0) { |
Josh Gao | d13def2 | 2016-03-02 16:11:13 -0800 | [diff] [blame] | 1248 | success = false; |
| 1249 | continue; |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1250 | } |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1251 | sc.ReportTransferRate(src_path, TransferDirection::pull); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1252 | } |
| 1253 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1254 | sc.ReportOverallTransferRate(TransferDirection::pull); |
Josh Gao | 5d093b2 | 2015-10-30 16:57:19 -0700 | [diff] [blame] | 1255 | return success; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1256 | } |
| 1257 | |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1258 | bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) { |
Elliott Hughes | b628cb1 | 2015-08-03 10:38:08 -0700 | [diff] [blame] | 1259 | SyncConnection sc; |
| 1260 | if (!sc.IsValid()) return false; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1261 | |
Josh Gao | a408fd2 | 2016-08-04 14:53:17 -0700 | [diff] [blame] | 1262 | bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only); |
| 1263 | if (!list_only) { |
| 1264 | sc.ReportOverallTransferRate(TransferDirection::push); |
| 1265 | } |
| 1266 | return success; |
The Android Open Source Project | 9ca14dc | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1267 | } |