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, UvError};
15 use rt::uv::status_to_maybe_uv_error;
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);
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));
88 pub fn unlink_sync<P: PathLike>(loop_: &Loop, path: &P) -> Result<int, UvError> {
89 let result = FsRequest::unlink_common(loop_, path, None);
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 }
142 fn sync_cleanup(result: int) -> Result<int, UvError> {
143 match status_to_maybe_uv_error(result as i32) {
144 Some(err) => Err(err),
149 pub struct FileDescriptor(c_int);
150 impl FileDescriptor {
151 fn new(fd: c_int) -> FileDescriptor {
156 pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor {
157 FileDescriptor::new(req.get_result())
160 // as per bnoordhuis in #libuv: offset >= 0 uses prwrite instead of write
161 fn write_common(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: Option<FsCallback>)
163 let complete_cb_ptr = match cb {
164 Some(_) => compl_cb as *u8,
167 let is_sync = cb.is_none();
168 let mut req = FsRequest::new(cb);
169 let base_ptr = buf.base as *c_void;
170 let len = buf.len as uint;
171 req.get_req_data().raw_fd = Some(self.native_handle());
172 let result = unsafe {
173 uvll::fs_write(loop_.native_handle(), req.native_handle(),
174 self.native_handle(), base_ptr,
175 len, offset, complete_cb_ptr) as int
177 if is_sync { req.cleanup_and_delete(); }
180 pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
181 self.write_common(loop_, buf, offset, Some(cb));
183 pub fn write_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64)
184 -> Result<int, UvError> {
185 let result = self.write_common(loop_, buf, offset, None);
189 fn read_common(&mut self, loop_: &Loop, buf: Buf,
190 offset: i64, cb: Option<FsCallback>)
192 let complete_cb_ptr = match cb {
193 Some(_) => compl_cb as *u8,
196 let is_sync = cb.is_none();
197 let mut req = FsRequest::new(cb);
198 req.get_req_data().raw_fd = Some(self.native_handle());
199 let buf_ptr = buf.base as *c_void;
200 let result = unsafe {
201 uvll::fs_read(loop_.native_handle(), req.native_handle(),
202 self.native_handle(), buf_ptr,
203 buf.len as uint, offset, complete_cb_ptr) as int
205 if is_sync { req.cleanup_and_delete(); }
208 pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
209 self.read_common(loop_, buf, offset, Some(cb));
211 pub fn read_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64)
212 -> Result<int, UvError> {
213 let result = self.read_common(loop_, buf, offset, None);
217 fn close_common(self, loop_: &Loop, cb: Option<FsCallback>) -> int {
218 let complete_cb_ptr = match cb {
219 Some(_) => compl_cb as *u8,
222 let is_sync = cb.is_none();
223 let req = FsRequest::new(cb);
224 let result = unsafe {
225 uvll::fs_close(loop_.native_handle(), req.native_handle(),
226 self.native_handle(), complete_cb_ptr) as int
228 if is_sync { req.cleanup_and_delete(); }
231 pub fn close(self, loop_: &Loop, cb: FsCallback) {
232 self.close_common(loop_, Some(cb));
234 pub fn close_sync(self, loop_: &Loop) -> Result<int, UvError> {
235 let result = self.close_common(loop_, None);
239 extern fn compl_cb(req: *uv_fs_t) {
240 let mut req: FsRequest = NativeHandle::from_native_handle(req);
241 // pull the user cb out of the req data
243 let data = req.get_req_data();
244 assert!(data.complete_cb.is_some());
245 // option dance, option dance. oooooh yeah.
246 data.complete_cb.take_unwrap()
248 // in uv_fs_open calls, the result will be the fd in the
249 // case of success, otherwise it's -1 indicating an error
250 let result = req.get_result();
251 let status = status_to_maybe_uv_error(result);
252 // we have a req and status, call the user cb..
253 // only giving the user a ref to the FsRequest, as we
254 // have to clean it up, afterwards (and they aren't really
256 cb(&mut req, status);
257 // clean up the req (and its data!) after calling the user cb
258 req.cleanup_and_delete();
261 impl NativeHandle<c_int> for FileDescriptor {
262 fn from_native_handle(handle: c_int) -> FileDescriptor {
263 FileDescriptor(handle)
265 fn native_handle(&self) -> c_int {
266 match self { &FileDescriptor(ptr) => ptr }
273 use libc::{STDOUT_FILENO};
276 use unstable::run_in_bare_thread;
278 use rt::uv::{Loop, Buf, slice_to_uv_buf};
279 use libc::{O_CREAT, O_RDWR, O_RDONLY,
280 S_IWUSR, S_IRUSR}; //NOTE: need defs for S_**GRP|S_**OTH in libc:: ...
283 fn file_test_full_simple_impl() {
284 do run_in_bare_thread {
285 let mut loop_ = Loop::new();
286 let create_flags = O_RDWR | O_CREAT;
287 let read_flags = O_RDONLY;
288 // 0644 BZZT! WRONG! 0600! See below.
289 let mode = S_IWUSR |S_IRUSR;
290 // these aren't defined in std::libc :(
291 //map_mode(S_IRGRP) |
293 let path_str = "./tmp/file_full_simple.txt";
294 let write_val = "hello".as_bytes().to_owned();
295 let write_buf = slice_to_uv_buf(write_val);
296 let write_buf_ptr: *Buf = &write_buf;
297 let read_buf_len = 1028;
298 let read_mem = vec::from_elem(read_buf_len, 0u8);
299 let read_buf = slice_to_uv_buf(read_mem);
300 let read_buf_ptr: *Buf = &read_buf;
301 let p = Path(path_str);
302 do FsRequest::open(&loop_, &p, create_flags as int, mode as int)
304 assert!(uverr.is_none());
305 let mut fd = FileDescriptor::from_open_req(req);
306 let raw_fd = fd.native_handle();
307 let buf = unsafe { *write_buf_ptr };
308 do fd.write(&req.get_loop(), buf, -1) |req, uverr| {
309 let fd = FileDescriptor(raw_fd);
310 do fd.close(&req.get_loop()) |req, _| {
311 let loop_ = req.get_loop();
312 assert!(uverr.is_none());
313 do FsRequest::open(&loop_, &Path(path_str), read_flags as int,0)
315 assert!(uverr.is_none());
316 let loop_ = req.get_loop();
317 let mut fd = FileDescriptor::from_open_req(req);
318 let raw_fd = fd.native_handle();
319 let read_buf = unsafe { *read_buf_ptr };
320 do fd.read(&loop_, read_buf, 0) |req, uverr| {
321 assert!(uverr.is_none());
322 let loop_ = req.get_loop();
323 // we know nread >=0 because uverr is none..
324 let nread = req.get_result() as uint;
325 // nread == 0 would be EOF
327 let read_str = unsafe {
328 let read_buf = *read_buf_ptr;
331 read_buf.base, nread))
333 assert!(read_str == ~"hello");
334 do FileDescriptor(raw_fd).close(&loop_) |req,uverr| {
335 assert!(uverr.is_none());
336 let loop_ = &req.get_loop();
337 do FsRequest::unlink(loop_, &Path(path_str))
339 assert!(uverr.is_none());
352 fn file_test_full_simple_impl_sync() {
353 do run_in_bare_thread {
355 let mut loop_ = Loop::new();
356 let create_flags = O_RDWR |
358 let read_flags = O_RDONLY;
364 let path_str = "./tmp/file_full_simple_sync.txt";
365 let write_val = "hello".as_bytes().to_owned();
366 let write_buf = slice_to_uv_buf(write_val);
368 let result = FsRequest::open_sync(&loop_, &Path(path_str),
369 create_flags as int, mode as int);
370 assert!(result.is_ok());
371 let mut fd = FileDescriptor(result.unwrap() as i32);
373 let result = fd.write_sync(&loop_, write_buf, -1);
374 assert!(result.is_ok());
376 let result = fd.close_sync(&loop_);
377 assert!(result.is_ok());
379 let result = FsRequest::open_sync(&loop_, &Path(path_str),
380 read_flags as int,0);
381 assert!(result.is_ok());
383 let mut fd = FileDescriptor(result.unwrap() as i32);
385 let read_mem: ~[u8] = vec::from_elem(len, 0u8);
386 let buf = slice_to_uv_buf(read_mem);
387 let result = fd.read_sync(&loop_, buf, 0);
388 assert!(result.is_ok());
389 let nread = result.unwrap();
390 // nread == 0 would be EOF.. we know it's >= zero because otherwise
391 // the above assert would fail
393 let read_str = str::from_bytes(
394 read_mem.slice(0, nread as uint));
395 assert!(read_str == ~"hello");
397 let result = fd.close_sync(&loop_);
398 assert!(result.is_ok());
400 let result = FsRequest::unlink_sync(&loop_, &Path(path_str));
401 assert!(result.is_ok());
402 } else { fail!("nread was 0.. wudn't expectin' that."); }
408 fn file_test_full_simple() {
409 file_test_full_simple_impl();
413 fn file_test_full_simple_sync() {
414 file_test_full_simple_impl_sync();
417 fn naive_print(loop_: &Loop, input: &str) {
418 let mut stdout = FileDescriptor(STDOUT_FILENO);
419 let write_val = input.as_bytes();
420 let write_buf = slice_to_uv_buf(write_val);
421 stdout.write_sync(loop_, write_buf, -1);
425 fn file_test_write_to_stdout() {
426 do run_in_bare_thread {
427 let mut loop_ = Loop::new();
428 naive_print(&loop_, "zanzibar!\n");