]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/uv/file.rs
0902d5e6d8c2085c08d59b27371bcd2494811ded
[rust.git] / src / libstd / rt / uv / file.rs
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.
4 //
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.
10
11 use prelude::*;
12 use ptr::null;
13 use libc::c_void;
14 use rt::uv::{Request, NativeHandle, Loop, FsCallback, Buf,
15              status_to_maybe_uv_error_with_loop, UvError};
16 use rt::uv::uvll;
17 use rt::uv::uvll::*;
18 use super::super::io::support::PathLike;
19 use cast::transmute;
20 use libc::{c_int};
21 use option::{None, Some, Option};
22
23 pub struct FsRequest(*uvll::uv_fs_t);
24 impl Request for FsRequest;
25
26 pub struct RequestData {
27     complete_cb: Option<FsCallback>,
28     raw_fd: Option<c_int>
29 }
30
31 impl FsRequest {
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);
37         fs_req
38     }
39
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,
44             None => 0 as *u8
45         };
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
52             })
53         });
54         if is_sync { req.cleanup_and_delete(); }
55         result
56     }
57     pub fn open<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int,
58                cb: FsCallback) {
59         FsRequest::open_common(loop_, path, flags, mode, Some(cb));
60     }
61
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)
66     }
67
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,
71             None => 0 as *u8
72         };
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
79             })
80         });
81         if is_sync { req.cleanup_and_delete(); }
82         result
83     }
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);
87     }
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)
91     }
92
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 {
96             complete_cb: cb,
97             raw_fd: None
98         };
99         unsafe {
100             let data = transmute::<~RequestData, *c_void>(data);
101             uvll::set_data_for_req(fs_req, data);
102         }
103     }
104
105     fn get_req_data<'r>(&'r mut self) -> &'r mut RequestData {
106         unsafe {
107             let data = uvll::get_data_for_req((self.native_handle()));
108             let data = transmute::<&*c_void, &mut ~RequestData>(&data);
109             return &mut **data;
110         }
111     }
112
113     pub fn get_result(&mut self) -> c_int {
114         unsafe {
115             uvll::get_result_from_fs_req(self.native_handle())
116         }
117     }
118
119     pub fn get_loop(&self) -> Loop {
120         unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} }
121     }
122
123     fn cleanup_and_delete(self) {
124         unsafe {
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)
130         }
131     }
132 }
133
134 impl NativeHandle<*uvll::uv_fs_t> for FsRequest {
135     fn from_native_handle(handle: *uvll:: uv_fs_t) -> FsRequest {
136         FsRequest(handle)
137     }
138     fn native_handle(&self) -> *uvll::uv_fs_t {
139         match self { &FsRequest(ptr) => ptr }
140     }
141 }
142
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),
147         None => Ok(result)
148     }
149 }
150
151 pub struct FileDescriptor(c_int);
152
153 impl FileDescriptor {
154     fn new(fd: c_int) -> FileDescriptor {
155         FileDescriptor(fd)
156     }
157
158
159     pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor {
160         FileDescriptor::new(req.get_result())
161     }
162
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>)
165           -> int {
166         let complete_cb_ptr = match cb {
167             Some(_) => compl_cb as *u8,
168             None => 0 as *u8
169         };
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
179         };
180         if is_sync { req.cleanup_and_delete(); }
181         result
182     }
183     pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
184         self.write_common(loop_, buf, offset, Some(cb));
185     }
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)
190     }
191
192     fn read_common(&mut self, loop_: &Loop, buf: Buf,
193                    offset: i64, cb: Option<FsCallback>)
194           -> int {
195         let complete_cb_ptr = match cb {
196             Some(_) => compl_cb as *u8,
197             None => 0 as *u8
198         };
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
207         };
208         if is_sync { req.cleanup_and_delete(); }
209         result
210     }
211     pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) {
212         self.read_common(loop_, buf, offset, Some(cb));
213     }
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)
218     }
219
220     fn close_common(self, loop_: &Loop, cb: Option<FsCallback>) -> int {
221         let complete_cb_ptr = match cb {
222             Some(_) => compl_cb as *u8,
223             None => 0 as *u8
224         };
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
230         };
231         if is_sync { req.cleanup_and_delete(); }
232         result
233     }
234     pub fn close(self, loop_: &Loop, cb: FsCallback) {
235         self.close_common(loop_, Some(cb));
236     }
237     pub fn close_sync(self, loop_: &Loop) -> Result<int, UvError> {
238         let result = self.close_common(loop_, None);
239         sync_cleanup(loop_, result)
240     }
241 }
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
246     let cb = {
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()
251     };
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
260     // reusable, anyways
261     cb(&mut req, status);
262     // clean up the req (and its data!) after calling the user cb
263     req.cleanup_and_delete();
264 }
265
266 impl NativeHandle<c_int> for FileDescriptor {
267     fn from_native_handle(handle: c_int) -> FileDescriptor {
268         FileDescriptor(handle)
269     }
270     fn native_handle(&self) -> c_int {
271         match self { &FileDescriptor(ptr) => ptr }
272     }
273 }
274
275 mod test {
276     use super::*;
277     //use rt::test::*;
278     use libc::{STDOUT_FILENO};
279     use vec;
280     use str;
281     use unstable::run_in_bare_thread;
282     use path::Path;
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:: ...
286                //S_IRGRP, S_IROTH};
287
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) |
297                 //map_mode(S_IROTH);
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)
308             |req, uverr| {
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)
319                             |req, uverr| {
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
331                                 if nread > 0 {
332                                     let read_str = unsafe {
333                                         let read_buf = *read_buf_ptr;
334                                         str::from_utf8(
335                                             vec::from_buf(
336                                                 read_buf.base, nread))
337                                     };
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))
343                                         |_,uverr| {
344                                             assert!(uverr.is_none());
345                                         };
346                                     };
347                                 }
348                             };
349                         };
350                     };
351                 };
352             };
353             loop_.run();
354             loop_.close();
355         }
356     }
357     fn file_test_full_simple_impl_sync() {
358         do run_in_bare_thread {
359             // setup
360             let mut loop_ = Loop::new();
361             let create_flags = O_RDWR |
362                 O_CREAT;
363             let read_flags = O_RDONLY;
364             // 0644
365             let mode = S_IWUSR |
366                 S_IRUSR;
367                 //S_IRGRP |
368                 //S_IROTH;
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);
372             // open/create
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);
377             // write
378             let result = fd.write_sync(&loop_, write_buf, -1);
379             assert!(result.is_ok());
380             // close
381             let result = fd.close_sync(&loop_);
382             assert!(result.is_ok());
383             // re-open
384             let result = FsRequest::open_sync(&loop_, &Path(path_str),
385                                                    read_flags as int,0);
386             assert!(result.is_ok());
387             let len = 1028;
388             let mut fd = FileDescriptor(result.unwrap() as i32);
389             // read
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
397             if nread > 0 {
398                 let read_str = str::from_utf8(
399                     read_mem.slice(0, nread as uint));
400                 assert!(read_str == ~"hello");
401                 // close
402                 let result = fd.close_sync(&loop_);
403                 assert!(result.is_ok());
404                 // unlink
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."); }
408             loop_.close();
409         }
410     }
411
412     #[test]
413     #[ignore(cfg(windows))] // FIXME #8814
414     fn file_test_full_simple() {
415         file_test_full_simple_impl();
416     }
417
418     #[test]
419     #[ignore(cfg(windows))] // FIXME #8814
420     fn file_test_full_simple_sync() {
421         file_test_full_simple_impl_sync();
422     }
423
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);
429     }
430
431     #[test]
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");
436             loop_.run();
437             loop_.close();
438         };
439     }
440 }