1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use rt::uv::{Request, NativeHandle, Loop, FsCallback, Buf,
15 status_to_maybe_uv_error_with_loop, UvError};
18 use super::super::io::support::PathLike;
21 use option::{None, Some, Option};
23 pub struct FsRequest(*uvll::uv_fs_t);
24 impl Request for FsRequest;
26 pub struct RequestData {
27 complete_cb: Option<FsCallback>,
32 pub fn new(cb: Option<FsCallback>) -> FsRequest {
33 let fs_req = unsafe { malloc_req(UV_FS) };
34 assert!(fs_req.is_not_null());
35 let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req);
36 fs_req.install_req_data(cb);
40 fn open_common<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int,
41 cb: Option<FsCallback>) -> int {
42 let complete_cb_ptr = match cb {
43 Some(_) => compl_cb as *u8,
46 let is_sync = cb.is_none();
47 let req = FsRequest::new(cb);
48 let result = path.path_as_str(|p| {
49 p.to_c_str().with_ref(|p| unsafe {
50 uvll::fs_open(loop_.native_handle(),
51 req.native_handle(), p, flags, mode, complete_cb_ptr) as int
54 if is_sync { req.cleanup_and_delete(); }
57 pub fn open<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int,
59 FsRequest::open_common(loop_, path, flags, mode, Some(cb));
62 pub fn open_sync<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int)
63 -> Result<int, UvError> {
64 let result = FsRequest::open_common(loop_, path, flags, mode, None);
65 sync_cleanup(loop_, result)
68 fn unlink_common<P: PathLike>(loop_: &Loop, path: &P, cb: Option<FsCallback>) -> int {
69 let complete_cb_ptr = match cb {
70 Some(_) => compl_cb as *u8,
73 let is_sync = cb.is_none();
74 let req = FsRequest::new(cb);
75 let result = path.path_as_str(|p| {
76 p.to_c_str().with_ref(|p| unsafe {
77 uvll::fs_unlink(loop_.native_handle(),
78 req.native_handle(), p, complete_cb_ptr) as int
81 if is_sync { req.cleanup_and_delete(); }
84 pub fn unlink<P: PathLike>(loop_: &Loop, path: &P, cb: FsCallback) {
85 let result = FsRequest::unlink_common(loop_, path, Some(cb));
86 sync_cleanup(loop_, result);
88 pub fn unlink_sync<P: PathLike>(loop_: &Loop, path: &P) -> Result<int, UvError> {
89 let result = FsRequest::unlink_common(loop_, path, None);
90 sync_cleanup(loop_, result)
93 pub fn install_req_data(&self, cb: Option<FsCallback>) {
94 let fs_req = (self.native_handle()) as *uvll::uv_write_t;
95 let data = ~RequestData {
100 let data = transmute::<~RequestData, *c_void>(data);
101 uvll::set_data_for_req(fs_req, data);
105 fn get_req_data<'r>(&'r mut self) -> &'r mut RequestData {
107 let data = uvll::get_data_for_req((self.native_handle()));
108 let data = transmute::<&*c_void, &mut ~RequestData>(&data);
113 pub fn get_result(&mut self) -> c_int {
115 uvll::get_result_from_fs_req(self.native_handle())
119 pub fn get_loop(&self) -> Loop {
120 unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} }
123 fn cleanup_and_delete(self) {
125 let data = uvll::get_data_for_req(self.native_handle());
126 let _data = transmute::<*c_void, ~RequestData>(data);
127 uvll::set_data_for_req(self.native_handle(), null::<()>());
128 uvll::fs_req_cleanup(self.native_handle());
129 free_req(self.native_handle() as *c_void)
134 impl NativeHandle<*uvll::uv_fs_t> for FsRequest {
135 fn from_native_handle(handle: *uvll:: uv_fs_t) -> FsRequest {
138 fn native_handle(&self) -> *uvll::uv_fs_t {
139 match self { &FsRequest(ptr) => ptr }
143 fn sync_cleanup(loop_: &Loop, result: int)
144 -> Result<int, UvError> {
145 match status_to_maybe_uv_error_with_loop(loop_.native_handle(), result as i32) {
146 Some(err) => Err(err),
151 pub struct FileDescriptor(c_int);
153 impl FileDescriptor {
154 fn new(fd: c_int) -> FileDescriptor {
159 pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor {
160 FileDescriptor::new(req.get_result())
163 // as per bnoordhuis in #libuv: offset >= 0 uses prwrite instead of write
164 fn write_common(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: Option<FsCallback>)
166 let complete_cb_ptr = match cb {
167 Some(_) => compl_cb as *u8,
170 let is_sync = cb.is_none();
171 let mut req = FsRequest::new(cb);
172 let base_ptr = buf.base as *c_void;
173 let len = buf.len as uint;
174 req.get_req_data().raw_fd = Some(self.native_handle());
175 let result = unsafe {
176 uvll::fs_write(loop_.native_handle(), req.native_handle(),
177 self.native_handle(), base_ptr,
178 len, offset, complete_cb_ptr) as int
180 if is_sync { req.cleanup_and_delete(); }
183 pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
184 self.write_common(loop_, buf, offset, Some(cb));
186 pub fn write_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64)
187 -> Result<int, UvError> {
188 let result = self.write_common(loop_, buf, offset, None);
189 sync_cleanup(loop_, result)
192 fn read_common(&mut self, loop_: &Loop, buf: Buf,
193 offset: i64, cb: Option<FsCallback>)
195 let complete_cb_ptr = match cb {
196 Some(_) => compl_cb as *u8,
199 let is_sync = cb.is_none();
200 let mut req = FsRequest::new(cb);
201 req.get_req_data().raw_fd = Some(self.native_handle());
202 let buf_ptr = buf.base as *c_void;
203 let result = unsafe {
204 uvll::fs_read(loop_.native_handle(), req.native_handle(),
205 self.native_handle(), buf_ptr,
206 buf.len as uint, offset, complete_cb_ptr) as int
208 if is_sync { req.cleanup_and_delete(); }
211 pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
212 self.read_common(loop_, buf, offset, Some(cb));
214 pub fn read_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64)
215 -> Result<int, UvError> {
216 let result = self.read_common(loop_, buf, offset, None);
217 sync_cleanup(loop_, result)
220 fn close_common(self, loop_: &Loop, cb: Option<FsCallback>) -> int {
221 let complete_cb_ptr = match cb {
222 Some(_) => compl_cb as *u8,
225 let is_sync = cb.is_none();
226 let req = FsRequest::new(cb);
227 let result = unsafe {
228 uvll::fs_close(loop_.native_handle(), req.native_handle(),
229 self.native_handle(), complete_cb_ptr) as int
231 if is_sync { req.cleanup_and_delete(); }
234 pub fn close(self, loop_: &Loop, cb: FsCallback) {
235 self.close_common(loop_, Some(cb));
237 pub fn close_sync(self, loop_: &Loop) -> Result<int, UvError> {
238 let result = self.close_common(loop_, None);
239 sync_cleanup(loop_, result)
242 extern fn compl_cb(req: *uv_fs_t) {
243 let mut req: FsRequest = NativeHandle::from_native_handle(req);
244 let loop_ = req.get_loop();
245 // pull the user cb out of the req data
247 let data = req.get_req_data();
248 assert!(data.complete_cb.is_some());
249 // option dance, option dance. oooooh yeah.
250 data.complete_cb.take_unwrap()
252 // in uv_fs_open calls, the result will be the fd in the
253 // case of success, otherwise it's -1 indicating an error
254 let result = req.get_result();
255 let status = status_to_maybe_uv_error_with_loop(
256 loop_.native_handle(), result);
257 // we have a req and status, call the user cb..
258 // only giving the user a ref to the FsRequest, as we
259 // have to clean it up, afterwards (and they aren't really
261 cb(&mut req, status);
262 // clean up the req (and its data!) after calling the user cb
263 req.cleanup_and_delete();
266 impl NativeHandle<c_int> for FileDescriptor {
267 fn from_native_handle(handle: c_int) -> FileDescriptor {
268 FileDescriptor(handle)
270 fn native_handle(&self) -> c_int {
271 match self { &FileDescriptor(ptr) => ptr }
278 use libc::{STDOUT_FILENO};
281 use unstable::run_in_bare_thread;
283 use rt::uv::{Loop, Buf, slice_to_uv_buf};
284 use libc::{O_CREAT, O_RDWR, O_RDONLY,
285 S_IWUSR, S_IRUSR}; //NOTE: need defs for S_**GRP|S_**OTH in libc:: ...
288 fn file_test_full_simple_impl() {
289 do run_in_bare_thread {
290 let mut loop_ = Loop::new();
291 let create_flags = O_RDWR | O_CREAT;
292 let read_flags = O_RDONLY;
293 // 0644 BZZT! WRONG! 0600! See below.
294 let mode = S_IWUSR |S_IRUSR;
295 // these aren't defined in std::libc :(
296 //map_mode(S_IRGRP) |
298 let path_str = "./tmp/file_full_simple.txt";
299 let write_val = "hello".as_bytes().to_owned();
300 let write_buf = slice_to_uv_buf(write_val);
301 let write_buf_ptr: *Buf = &write_buf;
302 let read_buf_len = 1028;
303 let read_mem = vec::from_elem(read_buf_len, 0u8);
304 let read_buf = slice_to_uv_buf(read_mem);
305 let read_buf_ptr: *Buf = &read_buf;
306 let p = Path(path_str);
307 do FsRequest::open(&loop_, &p, create_flags as int, mode as int)
309 assert!(uverr.is_none());
310 let mut fd = FileDescriptor::from_open_req(req);
311 let raw_fd = fd.native_handle();
312 let buf = unsafe { *write_buf_ptr };
313 do fd.write(&req.get_loop(), buf, -1) |req, uverr| {
314 let fd = FileDescriptor(raw_fd);
315 do fd.close(&req.get_loop()) |req, _| {
316 let loop_ = req.get_loop();
317 assert!(uverr.is_none());
318 do FsRequest::open(&loop_, &Path(path_str), read_flags as int,0)
320 assert!(uverr.is_none());
321 let loop_ = req.get_loop();
322 let mut fd = FileDescriptor::from_open_req(req);
323 let raw_fd = fd.native_handle();
324 let read_buf = unsafe { *read_buf_ptr };
325 do fd.read(&loop_, read_buf, 0) |req, uverr| {
326 assert!(uverr.is_none());
327 let loop_ = req.get_loop();
328 // we know nread >=0 because uverr is none..
329 let nread = req.get_result() as uint;
330 // nread == 0 would be EOF
332 let read_str = unsafe {
333 let read_buf = *read_buf_ptr;
336 read_buf.base, nread))
338 assert!(read_str == ~"hello");
339 do FileDescriptor(raw_fd).close(&loop_) |req,uverr| {
340 assert!(uverr.is_none());
341 let loop_ = &req.get_loop();
342 do FsRequest::unlink(loop_, &Path(path_str))
344 assert!(uverr.is_none());
357 fn file_test_full_simple_impl_sync() {
358 do run_in_bare_thread {
360 let mut loop_ = Loop::new();
361 let create_flags = O_RDWR |
363 let read_flags = O_RDONLY;
369 let path_str = "./tmp/file_full_simple_sync.txt";
370 let write_val = "hello".as_bytes().to_owned();
371 let write_buf = slice_to_uv_buf(write_val);
373 let result = FsRequest::open_sync(&loop_, &Path(path_str),
374 create_flags as int, mode as int);
375 assert!(result.is_ok());
376 let mut fd = FileDescriptor(result.unwrap() as i32);
378 let result = fd.write_sync(&loop_, write_buf, -1);
379 assert!(result.is_ok());
381 let result = fd.close_sync(&loop_);
382 assert!(result.is_ok());
384 let result = FsRequest::open_sync(&loop_, &Path(path_str),
385 read_flags as int,0);
386 assert!(result.is_ok());
388 let mut fd = FileDescriptor(result.unwrap() as i32);
390 let read_mem: ~[u8] = vec::from_elem(len, 0u8);
391 let buf = slice_to_uv_buf(read_mem);
392 let result = fd.read_sync(&loop_, buf, 0);
393 assert!(result.is_ok());
394 let nread = result.unwrap();
395 // nread == 0 would be EOF.. we know it's >= zero because otherwise
396 // the above assert would fail
398 let read_str = str::from_utf8(
399 read_mem.slice(0, nread as uint));
400 assert!(read_str == ~"hello");
402 let result = fd.close_sync(&loop_);
403 assert!(result.is_ok());
405 let result = FsRequest::unlink_sync(&loop_, &Path(path_str));
406 assert!(result.is_ok());
407 } else { fail!("nread was 0.. wudn't expectin' that."); }
413 #[ignore(cfg(windows))] // FIXME #8814
414 fn file_test_full_simple() {
415 file_test_full_simple_impl();
419 #[ignore(cfg(windows))] // FIXME #8814
420 fn file_test_full_simple_sync() {
421 file_test_full_simple_impl_sync();
424 fn naive_print(loop_: &Loop, input: &str) {
425 let mut stdout = FileDescriptor(STDOUT_FILENO);
426 let write_val = input.as_bytes();
427 let write_buf = slice_to_uv_buf(write_val);
428 stdout.write_sync(loop_, write_buf, -1);
432 fn file_test_write_to_stdout() {
433 do run_in_bare_thread {
434 let mut loop_ = Loop::new();
435 naive_print(&loop_, "zanzibar!\n");