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