Merge "adb: don't divide by zero"
diff --git a/adb_listeners.cpp b/adb_listeners.cpp
index d5b1fd5..1b75090 100644
--- a/adb_listeners.cpp
+++ b/adb_listeners.cpp
@@ -33,21 +33,17 @@
 };
 
 static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
-    asocket *s;
-
-    if(ev & FDE_READ) {
+    if (ev & FDE_READ) {
         struct sockaddr addr;
-        socklen_t alen;
-        int fd;
+        socklen_t alen = sizeof(addr);
+        int fd = adb_socket_accept(_fd, &addr, &alen);
+        if (fd < 0) return;
 
-        alen = sizeof(addr);
-        fd = adb_socket_accept(_fd, &addr, &alen);
-        if(fd < 0) return;
+        int rcv_buf_size = CHUNK_SIZE;
+        adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
 
-        adb_socket_setbufsize(fd, CHUNK_SIZE);
-
-        s = create_local_socket(fd);
-        if(s) {
+        asocket* s = create_local_socket(fd);
+        if (s) {
             connect_to_smartsocket(s);
             return;
         }
diff --git a/file_sync_client.cpp b/file_sync_client.cpp
index c422b4f..9ad7bad 100644
--- a/file_sync_client.cpp
+++ b/file_sync_client.cpp
@@ -103,7 +103,7 @@
     // Sending header, payload, and footer in a single write makes a huge
     // difference to "adb sync" performance.
     bool SendSmallFile(const char* path_and_mode,
-                       const char* rpath,
+                       const char* lpath, const char* rpath,
                        unsigned mtime,
                        const char* data, size_t data_length) {
         Print(rpath);
@@ -139,8 +139,7 @@
         req_done->path_length = mtime;
         p += sizeof(SyncRequest);
 
-        if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
-
+        WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
         total_bytes += data_length;
         return true;
     }
@@ -164,31 +163,27 @@
 
         int lfd = adb_open(lpath, O_RDONLY);
         if (lfd < 0) {
-            Error("cannot open '%s': %s", lpath, strerror(errno));
+            Error("opening '%s' locally failed: %s", lpath, strerror(errno));
             return false;
         }
 
         syncsendbuf sbuf;
         sbuf.id = ID_DATA;
         while (true) {
-            int ret = adb_read(lfd, sbuf.data, max);
-            if (ret <= 0) {
-                if (ret < 0) {
-                    Error("cannot read '%s': %s", lpath, strerror(errno));
-                    adb_close(lfd);
-                    return false;
-                }
+            int bytes_read = adb_read(lfd, sbuf.data, max);
+            if (bytes_read == -1) {
+                Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+                adb_close(lfd);
+                return false;
+            } else if (bytes_read == 0) {
                 break;
             }
 
-            sbuf.size = ret;
-            if (!WriteFdExactly(fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
-                adb_close(lfd);
-                return false;
-            }
-            total_bytes += ret;
+            sbuf.size = bytes_read;
+            WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
 
-            bytes_copied += ret;
+            total_bytes += bytes_read;
+            bytes_copied += bytes_read;
 
             if (total_size == 0) {
                 // This case can happen if we're racing against something that wrote to the file
@@ -206,12 +201,7 @@
         syncmsg msg;
         msg.data.id = ID_DONE;
         msg.data.size = mtime;
-        if (!WriteFdExactly(fd, &msg.data, sizeof(msg.data))) {
-            Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
-            return false;
-        }
-
-        return true;
+        return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
     }
 
     bool CopyDone(const char* from, const char* to) {
@@ -304,6 +294,27 @@
         return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
     }
 
+    bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+        if (!WriteFdExactly(fd, data, data_length)) {
+            if (errno == ECONNRESET) {
+                // Assume adbd told us why it was closing the connection, and
+                // try to read failure reason from adbd.
+                syncmsg msg;
+                if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+                    Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+                } else if (msg.status.id != ID_FAIL) {
+                    Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+                } else {
+                    ReportCopyFailure(from, to, msg);
+                }
+            } else {
+                Error("%zu-byte write failed: %s", data_length, strerror(errno));
+            }
+            _exit(1);
+        }
+        return true;
+    }
+
     static uint64_t CurrentTimeMs() {
         struct timeval tv;
         gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
@@ -369,7 +380,9 @@
         }
         buf[data_length++] = '\0';
 
-        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, mtime, buf, data_length)) return false;
+        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+            return false;
+        }
         return sc.CopyDone(lpath, rpath);
 #endif
     }
@@ -390,7 +403,8 @@
             sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
             return false;
         }
-        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, mtime, data.data(), data.size())) {
+        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
+                              data.data(), data.size())) {
             return false;
         }
     } else {
@@ -565,11 +579,12 @@
         } else {
             if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
                 sc.Error("skipping special file '%s'", lpath.c_str());
+                ci.skip = true;
             } else {
                 ci.time = st.st_mtime;
                 ci.size = st.st_size;
-                filelist->push_back(ci);
             }
+            filelist->push_back(ci);
         }
     }
 
@@ -736,8 +751,7 @@
     bool empty_dir = true;
 
     // Put the files/dirs in rpath on the lists.
-    auto callback = [&](unsigned mode, unsigned size, unsigned time,
-                        const char* name) {
+    auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
         if (IsDotOrDotDot(name)) {
             return;
         }
@@ -748,12 +762,18 @@
         copyinfo ci = mkcopyinfo(lpath, rpath, name, mode);
         if (S_ISDIR(mode)) {
             dirlist.push_back(ci);
-        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
-            ci.time = time;
-            ci.size = size;
-            filelist->push_back(ci);
         } else {
-            sc.Warning("skipping special file '%s'\n", name);
+            if (S_ISREG(mode)) {
+                ci.time = time;
+                ci.size = size;
+            } else if (S_ISLNK(mode)) {
+                sc.Warning("skipping symlink '%s'", name);
+                ci.skip = true;
+            } else {
+                sc.Warning("skipping special file '%s'", name);
+                ci.skip = true;
+            }
+            filelist->push_back(ci);
         }
     };
 
diff --git a/file_sync_service.cpp b/file_sync_service.cpp
index 7484a7c..945fa5a 100644
--- a/file_sync_service.cpp
+++ b/file_sync_service.cpp
@@ -394,6 +394,18 @@
 void file_sync_service(int fd, void* cookie) {
     std::vector<char> buffer(SYNC_DATA_MAX);
 
+    // If there's a problem on the device, we'll send an ID_FAIL message and
+    // close the socket. Unfortunately the kernel will sometimes throw that
+    // data away if the other end keeps writing without reading (which is
+    // the normal case with our protocol --- they won't read until the end).
+    // So set SO_LINGER to give the client 20s to get around to reading our
+    // failure response. Without this, the other side's ability to report
+    // useful errors is reduced.
+    struct linger l;
+    l.l_onoff = 1;
+    l.l_linger = 20;
+    adb_setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+
     while (handle_sync_command(fd, buffer)) {
     }
 
diff --git a/sysdeps.h b/sysdeps.h
index cba66fc..22c9b39 100644
--- a/sysdeps.h
+++ b/sysdeps.h
@@ -242,18 +242,6 @@
 #undef   setsockopt
 #define  setsockopt  ___xxx_setsockopt
 
-static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
-{
-    int opt = bufsize;
-    return adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*)&opt, sizeof(opt));
-}
-
-static __inline__ void  disable_tcp_nagle( int  fd )
-{
-    int  on = 1;
-    adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on));
-}
-
 extern int  adb_socketpair( int  sv[2] );
 
 static __inline__ int adb_is_absolute_host_path(const char* path) {
@@ -670,18 +658,6 @@
 #endif
 }
 
-static __inline__  int  adb_socket_setbufsize(int fd, int  bufsize )
-{
-    int opt = bufsize;
-    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
-}
-
-static __inline__ void  disable_tcp_nagle(int fd)
-{
-    int  on = 1;
-    setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
-}
-
 static __inline__ int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
 {
     return setsockopt( fd, level, optname, optval, optlen );
@@ -739,4 +715,9 @@
 
 #endif /* !_WIN32 */
 
+static inline void disable_tcp_nagle(int fd) {
+    int off = 1;
+    adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+}
+
 #endif /* _ADB_SYSDEPS_H */