about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-03-25 09:37:36 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-03-25 09:37:36 -0700
commit5fddb4280e950c28916ac21838f7433cd199aaed (patch)
tree6c8ed74894051ec58ca1d21e2f8f0f5eca5d4666
parent1f5571abc222520537daa00fc8256040647eec86 (diff)
downloadrust-5fddb4280e950c28916ac21838f7433cd199aaed.tar.gz
rust-5fddb4280e950c28916ac21838f7433cd199aaed.zip
rustuv: Handle short writes in uv_fs_write
The libuv fs wrappers are very thin wrappers around the syscalls they correspond
to, and a notable worrisome case is the write syscall. This syscall is not
guaranteed to write the entire buffer provided, so we may have to continue
calling uv_fs_write if a short write occurs.

Closes #13130
-rw-r--r--src/librustuv/file.rs37
-rw-r--r--src/librustuv/uvll.rs4
-rw-r--r--src/rt/rust_uv.c2
3 files changed, 32 insertions, 11 deletions
diff --git a/src/librustuv/file.rs b/src/librustuv/file.rs
index 45e7ba4d21f..d5235a9fe56 100644
--- a/src/librustuv/file.rs
+++ b/src/librustuv/file.rs
@@ -12,7 +12,7 @@ use std::c_str::CString;
 use std::c_str;
 use std::cast::transmute;
 use std::cast;
-use std::libc::{c_int, c_char, c_void, size_t};
+use std::libc::{c_int, c_char, c_void, size_t, ssize_t};
 use std::libc;
 use std::rt::task::BlockedTask;
 use std::io::{FileStat, IoError};
@@ -74,11 +74,32 @@ impl FsRequest {
     pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
         -> Result<(), UvError>
     {
-        execute_nop(|req, cb| unsafe {
-            uvll::uv_fs_write(loop_.handle, req,
-                              fd, buf.as_ptr() as *c_void,
-                              buf.len() as size_t, offset, cb)
-        })
+        // In libuv, uv_fs_write is basically just shelling out to a write()
+        // syscall at some point, with very little fluff around it. This means
+        // that write() could actually be a short write, so we need to be sure
+        // to call it continuously if we get a short write back. This method is
+        // expected to write the full data if it returns success.
+        let mut written = 0;
+        while written < buf.len() {
+            let offset = if offset == -1 {
+                offset
+            } else {
+                offset + written as i64
+            };
+            match execute(|req, cb| unsafe {
+                uvll::uv_fs_write(loop_.handle,
+                                  req,
+                                  fd,
+                                  buf.as_ptr().offset(written as int) as *c_void,
+                                  (buf.len() - written) as size_t,
+                                  offset,
+                                  cb)
+            }).map(|req| req.get_result()) {
+                Err(e) => return Err(e),
+                Ok(n) => { written += n as uint; }
+            }
+        }
+        Ok(())
     }
 
     pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
@@ -227,7 +248,7 @@ impl FsRequest {
         })
     }
 
-    pub fn get_result(&self) -> c_int {
+    pub fn get_result(&self) -> ssize_t {
         unsafe { uvll::get_result_from_fs_req(self.req) }
     }
 
@@ -309,7 +330,7 @@ fn execute(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
                 unsafe { uvll::set_data_for_req(req.req, &slot) }
             });
             match req.get_result() {
-                n if n < 0 => Err(UvError(n)),
+                n if n < 0 => Err(UvError(n as i32)),
                 _ => Ok(req),
             }
         }
diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs
index b9b7ed13cc1..25f4fb0edf8 100644
--- a/src/librustuv/uvll.rs
+++ b/src/librustuv/uvll.rs
@@ -426,7 +426,7 @@ pub unsafe fn set_stdio_container_stream(c: *uv_stdio_container_t,
 }
 
 // data access helpers
-pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int {
+pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> ssize_t {
     rust_uv_get_result_from_fs_req(req)
 }
 pub unsafe fn get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void {
@@ -501,7 +501,7 @@ extern {
     fn rust_uv_get_udp_handle_from_send_req(req: *uv_udp_send_t) -> *uv_udp_t;
 
     fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t);
-    fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int;
+    fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> ssize_t;
     fn rust_uv_get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void;
     fn rust_uv_get_path_from_fs_req(req: *uv_fs_t) -> *c_char;
     fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t;
diff --git a/src/rt/rust_uv.c b/src/rt/rust_uv.c
index c8f041c6562..0739e8b5e11 100644
--- a/src/rt/rust_uv.c
+++ b/src/rt/rust_uv.c
@@ -97,7 +97,7 @@ rust_uv_req_type_max() {
   return UV_REQ_TYPE_MAX;
 }
 
-int
+ssize_t
 rust_uv_get_result_from_fs_req(uv_fs_t* req) {
   return req->result;
 }