(index<- ) ./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, 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;
21 use libc::{c_int};
22 use option::{None, Some, Option};
23
24 pub struct FsRequest(*uvll::uv_fs_t);
25 impl Request for FsRequest {}
26
27 pub struct RequestData {
28 complete_cb: Option<FsCallback>
29 }
30
31 impl FsRequest {
32 pub fn new() -> 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
37 }
38
39 pub fn open<P: PathLike>(self, loop_: &Loop, path: &P, flags: int, mode: int,
40 cb: FsCallback) {
41 let complete_cb_ptr = {
42 let mut me = self;
43 me.req_boilerplate(Some(cb))
44 };
45 path.path_as_str(|p| {
46 p.with_c_str(|p| unsafe {
47 uvll::fs_open(loop_.native_handle(),
48 self.native_handle(), p, flags, mode, complete_cb_ptr)
49 })
50 });
51 }
52
53 pub fn open_sync<P: PathLike>(self, loop_: &Loop, path: &P,
54 flags: int, mode: int) -> Result<c_int, UvError> {
55 let complete_cb_ptr = {
56 let mut me = self;
57 me.req_boilerplate(None)
58 };
59 let result = path.path_as_str(|p| {
60 p.with_c_str(|p| unsafe {
61 uvll::fs_open(loop_.native_handle(),
62 self.native_handle(), p, flags, mode, complete_cb_ptr)
63 })
64 });
65 self.sync_cleanup(result)
66 }
67
68 pub fn unlink<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) {
69 let complete_cb_ptr = {
70 let mut me = self;
71 me.req_boilerplate(Some(cb))
72 };
73 path.path_as_str(|p| {
74 p.with_c_str(|p| unsafe {
75 uvll::fs_unlink(loop_.native_handle(),
76 self.native_handle(), p, complete_cb_ptr)
77 })
78 });
79 }
80
81 pub fn unlink_sync<P: PathLike>(self, loop_: &Loop, path: &P)
82 -> Result<c_int, UvError> {
83 let complete_cb_ptr = {
84 let mut me = self;
85 me.req_boilerplate(None)
86 };
87 let result = path.path_as_str(|p| {
88 p.with_c_str(|p| unsafe {
89 uvll::fs_unlink(loop_.native_handle(),
90 self.native_handle(), p, complete_cb_ptr)
91 })
92 });
93 self.sync_cleanup(result)
94 }
95
96 pub fn stat<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) {
97 let complete_cb_ptr = {
98 let mut me = self;
99 me.req_boilerplate(Some(cb))
100 };
101 path.path_as_str(|p| {
102 p.with_c_str(|p| unsafe {
103 uvll::fs_stat(loop_.native_handle(),
104 self.native_handle(), p, complete_cb_ptr)
105 })
106 });
107 }
108
109 pub fn write(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) {
110 let complete_cb_ptr = {
111 let mut me = self;
112 me.req_boilerplate(Some(cb))
113 };
114 let base_ptr = buf.base as *c_void;
115 let len = buf.len as uint;
116 unsafe {
117 uvll::fs_write(loop_.native_handle(), self.native_handle(),
118 fd, base_ptr,
119 len, offset, complete_cb_ptr)
120 };
121 }
122 pub fn write_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64)
123 -> Result<c_int, UvError> {
124 let complete_cb_ptr = {
125 let mut me = self;
126 me.req_boilerplate(None)
127 };
128 let base_ptr = buf.base as *c_void;
129 let len = buf.len as uint;
130 let result = unsafe {
131 uvll::fs_write(loop_.native_handle(), self.native_handle(),
132 fd, base_ptr,
133 len, offset, complete_cb_ptr)
134 };
135 self.sync_cleanup(result)
136 }
137
138 pub fn read(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) {
139 let complete_cb_ptr = {
140 let mut me = self;
141 me.req_boilerplate(Some(cb))
142 };
143 let buf_ptr = buf.base as *c_void;
144 let len = buf.len as uint;
145 unsafe {
146 uvll::fs_read(loop_.native_handle(), self.native_handle(),
147 fd, buf_ptr,
148 len, offset, complete_cb_ptr)
149 };
150 }
151 pub fn read_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64)
152 -> Result<c_int, UvError> {
153 let complete_cb_ptr = {
154 let mut me = self;
155 me.req_boilerplate(None)
156 };
157 let buf_ptr = buf.base as *c_void;
158 let len = buf.len as uint;
159 let result = unsafe {
160 uvll::fs_read(loop_.native_handle(), self.native_handle(),
161 fd, buf_ptr,
162 len, offset, complete_cb_ptr)
163 };
164 self.sync_cleanup(result)
165 }
166
167 pub fn close(self, loop_: &Loop, fd: c_int, cb: FsCallback) {
168 let complete_cb_ptr = {
169 let mut me = self;
170 me.req_boilerplate(Some(cb))
171 };
172 unsafe {
173 uvll::fs_close(loop_.native_handle(), self.native_handle(),
174 fd, complete_cb_ptr)
175 };
176 }
177 pub fn close_sync(self, loop_: &Loop, fd: c_int) -> Result<c_int, UvError> {
178 let complete_cb_ptr = {
179 let mut me = self;
180 me.req_boilerplate(None)
181 };
182 let result = unsafe {
183 uvll::fs_close(loop_.native_handle(), self.native_handle(),
184 fd, complete_cb_ptr)
185 };
186 self.sync_cleanup(result)
187 }
188
189 pub fn mkdir<P: PathLike>(self, loop_: &Loop, path: &P, mode: int, cb: FsCallback) {
190 let complete_cb_ptr = {
191 let mut me = self;
192 me.req_boilerplate(Some(cb))
193 };
194 path.path_as_str(|p| {
195 p.with_c_str(|p| unsafe {
196 uvll::fs_mkdir(loop_.native_handle(),
197 self.native_handle(), p, mode, complete_cb_ptr)
198 })
199 });
200 }
201
202 pub fn rmdir<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) {
203 let complete_cb_ptr = {
204 let mut me = self;
205 me.req_boilerplate(Some(cb))
206 };
207 path.path_as_str(|p| {
208 p.with_c_str(|p| unsafe {
209 uvll::fs_rmdir(loop_.native_handle(),
210 self.native_handle(), p, complete_cb_ptr)
211 })
212 });
213 }
214
215 pub fn readdir<P: PathLike>(self, loop_: &Loop, path: &P,
216 flags: c_int, cb: FsCallback) {
217 let complete_cb_ptr = {
218 let mut me = self;
219 me.req_boilerplate(Some(cb))
220 };
221 path.path_as_str(|p| {
222 p.with_c_str(|p| unsafe {
223 uvll::fs_readdir(loop_.native_handle(),
224 self.native_handle(), p, flags, complete_cb_ptr)
225 })
226 });
227 }
228
229 // accessors/utility funcs
230 fn sync_cleanup(self, result: c_int)
231 -> Result<c_int, UvError> {
232 self.cleanup_and_delete();
233 match status_to_maybe_uv_error(result as i32) {
234 Some(err) => Err(err),
235 None => Ok(result)
236 }
237 }
238 fn req_boilerplate(&mut self, cb: Option<FsCallback>) -> *u8 {
239 let result = match cb {
240 Some(_) => {
241 compl_cb as *u8
242 },
243 None => 0 as *u8
244 };
245 self.install_req_data(cb);
246 result
247 }
248 pub fn install_req_data(&mut self, cb: Option<FsCallback>) {
249 let fs_req = (self.native_handle()) as *uvll::uv_write_t;
250 let data = ~RequestData {
251 complete_cb: cb
252 };
253 unsafe {
254 let data = transmute::<~RequestData, *c_void>(data);
255 uvll::set_data_for_req(fs_req, data);
256 }
257 }
258
259 fn get_req_data<'r>(&'r mut self) -> &'r mut RequestData {
260 unsafe {
261 let data = uvll::get_data_for_req((self.native_handle()));
262 let data = transmute::<&*c_void, &mut ~RequestData>(&data);
263 &mut **data
264 }
265 }
266
267 pub fn get_result(&mut self) -> c_int {
268 unsafe {
269 uvll::get_result_from_fs_req(self.native_handle())
270 }
271 }
272
273 pub fn get_loop(&self) -> Loop {
274 unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} }
275 }
276
277 pub fn get_stat(&self) -> uv_stat_t {
278 let stat = uv_stat_t::new();
279 unsafe { uvll::populate_stat(self.native_handle(), &stat); }
280 stat
281 }
282
283 pub fn get_ptr(&self) -> *libc::c_void {
284 unsafe {
285 uvll::get_ptr_from_fs_req(self.native_handle())
286 }
287 }
288
289 pub fn get_paths(&mut self) -> ~[~str] {
290 use str;
291 let ptr = self.get_ptr();
292 match self.get_result() {
293 n if (n <= 0) => {
294 ~[]
295 },
296 n => {
297 let n_len = n as uint;
298 // we pass in the len that uv tells us is there
299 // for the entries and we don't continue past that..
300 // it appears that sometimes the multistring isn't
301 // correctly delimited and we stray into garbage memory?
302 // in any case, passing Some(n_len) fixes it and ensures
303 // good results
304 let raw_path_strs = unsafe {
305 str::raw::from_c_multistring(ptr as *libc::c_char, Some(n_len)) };
306 let raw_len = raw_path_strs.len();
307 assert_eq!(raw_len, n_len);
308 raw_path_strs
309 }
310 }
311 }
312
313 fn cleanup_and_delete(self) {
314 unsafe {
315 let data = uvll::get_data_for_req(self.native_handle());
316 let _data = transmute::<*c_void, ~RequestData>(data);
317 uvll::set_data_for_req(self.native_handle(), null::<()>());
318 uvll::fs_req_cleanup(self.native_handle());
319 free_req(self.native_handle() as *c_void)
320 }
321 }
322 }
323
324 impl NativeHandle<*uvll::uv_fs_t> for FsRequest {
325 fn from_native_handle(handle: *uvll:: uv_fs_t) -> FsRequest {
326 FsRequest(handle)
327 }
328 fn native_handle(&self) -> *uvll::uv_fs_t {
329 match self { &FsRequest(ptr) => ptr }
330 }
331 }
332
333 fn sync_cleanup(result: int)
334 -> Result<int, UvError> {
335 match status_to_maybe_uv_error(result as i32) {
336 Some(err) => Err(err),
337 None => Ok(result)
338 }
339 }
340
341 extern fn compl_cb(req: *uv_fs_t) {
342 let mut req: FsRequest = NativeHandle::from_native_handle(req);
343 // pull the user cb out of the req data
344 let cb = {
345 let data = req.get_req_data();
346 assert!(data.complete_cb.is_some());
347 // option dance, option dance. oooooh yeah.
348 data.complete_cb.take_unwrap()
349 };
350 // in uv_fs_open calls, the result will be the fd in the
351 // case of success, otherwise it's -1 indicating an error
352 let result = req.get_result();
353 let status = status_to_maybe_uv_error(result);
354 // we have a req and status, call the user cb..
355 // only giving the user a ref to the FsRequest, as we
356 // have to clean it up, afterwards (and they aren't really
357 // reusable, anyways
358 cb(&mut req, status);
359 // clean up the req (and its data!) after calling the user cb
360 req.cleanup_and_delete();
361 }
362
363 #[cfg(test)]
364 mod test {
365 use super::*;
366 //use rt::test::*;
367 use libc::{STDOUT_FILENO};
368 use vec;
369 use str;
370 use unstable::run_in_bare_thread;
371 use path::Path;
372 use rt::uv::{Loop, Buf, slice_to_uv_buf};
373 use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
374
375 #[test]
376 fn file_test_full_simple() {
377 do run_in_bare_thread {
378 let mut loop_ = Loop::new();
379 let create_flags = O_RDWR | O_CREAT;
380 let read_flags = O_RDONLY;
381 // 0644 BZZT! WRONG! 0600! See below.
382 let mode = S_IWUSR |S_IRUSR;
383 // these aren't defined in std::libc :(
384 //map_mode(S_IRGRP) |
385 //map_mode(S_IROTH);
386 let path_str = "./tmp/file_full_simple.txt";
387 let write_val = "hello".as_bytes().to_owned();
388 let write_buf = slice_to_uv_buf(write_val);
389 let write_buf_ptr: *Buf = &write_buf;
390 let read_buf_len = 1028;
391 let read_mem = vec::from_elem(read_buf_len, 0u8);
392 let read_buf = slice_to_uv_buf(read_mem);
393 let read_buf_ptr: *Buf = &read_buf;
394 let p = Path::new(path_str);
395 let open_req = FsRequest::new();
396 do open_req.open(&loop_, &p, create_flags as int, mode as int)
397 |req, uverr| {
398 assert!(uverr.is_none());
399 let fd = req.get_result();
400 let buf = unsafe { *write_buf_ptr };
401 let write_req = FsRequest::new();
402 do write_req.write(&req.get_loop(), fd, buf, -1) |req, uverr| {
403 let close_req = FsRequest::new();
404 do close_req.close(&req.get_loop(), fd) |req, _| {
405 assert!(uverr.is_none());
406 let loop_ = req.get_loop();
407 let open_req = FsRequest::new();
408 do open_req.open(&loop_, &Path::new(path_str), read_flags as int,0)
409 |req, uverr| {
410 assert!(uverr.is_none());
411 let loop_ = req.get_loop();
412 let fd = req.get_result();
413 let read_buf = unsafe { *read_buf_ptr };
414 let read_req = FsRequest::new();
415 do read_req.read(&loop_, fd, read_buf, 0) |req, uverr| {
416 assert!(uverr.is_none());
417 let loop_ = req.get_loop();
418 // we know nread >=0 because uverr is none..
419 let nread = req.get_result() as uint;
420 // nread == 0 would be EOF
421 if nread > 0 {
422 let read_str = unsafe {
423 let read_buf = *read_buf_ptr;
424 str::from_utf8(
425 vec::from_buf(
426 read_buf.base, nread))
427 };
428 assert!(read_str == ~"hello");
429 let close_req = FsRequest::new();
430 do close_req.close(&loop_, fd) |req,uverr| {
431 assert!(uverr.is_none());
432 let loop_ = &req.get_loop();
433 let unlink_req = FsRequest::new();
434 do unlink_req.unlink(loop_, &Path::new(path_str))
435 |_,uverr| {
436 assert!(uverr.is_none());
437 };
438 };
439 };
440 };
441 };
442 };
443 };
444 };
445 loop_.run();
446 loop_.close();
447 }
448 }
449
450 #[test]
451 fn file_test_full_simple_sync() {
452 do run_in_bare_thread {
453 // setup
454 let mut loop_ = Loop::new();
455 let create_flags = O_RDWR |
456 O_CREAT;
457 let read_flags = O_RDONLY;
458 // 0644
459 let mode = S_IWUSR |
460 S_IRUSR;
461 //S_IRGRP |
462 //S_IROTH;
463 let path_str = "./tmp/file_full_simple_sync.txt";
464 let write_val = "hello".as_bytes().to_owned();
465 let write_buf = slice_to_uv_buf(write_val);
466 // open/create
467 let open_req = FsRequest::new();
468 let result = open_req.open_sync(&loop_, &Path::new(path_str),
469 create_flags as int, mode as int);
470 assert!(result.is_ok());
471 let fd = result.unwrap();
472 // write
473 let write_req = FsRequest::new();
474 let result = write_req.write_sync(&loop_, fd, write_buf, -1);
475 assert!(result.is_ok());
476 // close
477 let close_req = FsRequest::new();
478 let result = close_req.close_sync(&loop_, fd);
479 assert!(result.is_ok());
480 // re-open
481 let open_req = FsRequest::new();
482 let result = open_req.open_sync(&loop_, &Path::new(path_str),
483 read_flags as int,0);
484 assert!(result.is_ok());
485 let len = 1028;
486 let fd = result.unwrap();
487 // read
488 let read_mem: ~[u8] = vec::from_elem(len, 0u8);
489 let buf = slice_to_uv_buf(read_mem);
490 let read_req = FsRequest::new();
491 let result = read_req.read_sync(&loop_, fd, buf, 0);
492 assert!(result.is_ok());
493 let nread = result.unwrap();
494 // nread == 0 would be EOF.. we know it's >= zero because otherwise
495 // the above assert would fail
496 if nread > 0 {
497 let read_str = str::from_utf8(
498 read_mem.slice(0, nread as uint));
499 assert!(read_str == ~"hello");
500 // close
501 let close_req = FsRequest::new();
502 let result = close_req.close_sync(&loop_, fd);
503 assert!(result.is_ok());
504 // unlink
505 let unlink_req = FsRequest::new();
506 let result = unlink_req.unlink_sync(&loop_, &Path::new(path_str));
507 assert!(result.is_ok());
508 } else { fail2!("nread was 0.. wudn't expectin' that."); }
509 loop_.close();
510 }
511 }
512
513 fn naive_print(loop_: &Loop, input: &str) {
514 let write_val = input.as_bytes();
515 let write_buf = slice_to_uv_buf(write_val);
516 let write_req = FsRequest::new();
517 write_req.write_sync(loop_, STDOUT_FILENO, write_buf, -1);
518 }
519
520 #[test]
521 fn file_test_write_to_stdout() {
522 do run_in_bare_thread {
523 let mut loop_ = Loop::new();
524 naive_print(&loop_, "zanzibar!\n");
525 loop_.run();
526 loop_.close();
527 };
528 }
529 #[test]
530 fn file_test_stat_simple() {
531 do run_in_bare_thread {
532 let mut loop_ = Loop::new();
533 let path = "./tmp/file_test_stat_simple.txt";
534 let create_flags = O_RDWR |
535 O_CREAT;
536 let mode = S_IWUSR |
537 S_IRUSR;
538 let write_val = "hello".as_bytes().to_owned();
539 let write_buf = slice_to_uv_buf(write_val);
540 let write_buf_ptr: *Buf = &write_buf;
541 let open_req = FsRequest::new();
542 do open_req.open(&loop_, &path, create_flags as int, mode as int)
543 |req, uverr| {
544 assert!(uverr.is_none());
545 let fd = req.get_result();
546 let buf = unsafe { *write_buf_ptr };
547 let write_req = FsRequest::new();
548 do write_req.write(&req.get_loop(), fd, buf, 0) |req, uverr| {
549 assert!(uverr.is_none());
550 let loop_ = req.get_loop();
551 let stat_req = FsRequest::new();
552 do stat_req.stat(&loop_, &path) |req, uverr| {
553 assert!(uverr.is_none());
554 let loop_ = req.get_loop();
555 let stat = req.get_stat();
556 let sz: uint = stat.st_size as uint;
557 assert!(sz > 0);
558 let close_req = FsRequest::new();
559 do close_req.close(&loop_, fd) |req, uverr| {
560 assert!(uverr.is_none());
561 let loop_ = req.get_loop();
562 let unlink_req = FsRequest::new();
563 do unlink_req.unlink(&loop_, &path) |req,uverr| {
564 assert!(uverr.is_none());
565 let loop_ = req.get_loop();
566 let stat_req = FsRequest::new();
567 do stat_req.stat(&loop_, &path) |_, uverr| {
568 // should cause an error because the
569 // file doesn't exist anymore
570 assert!(uverr.is_some());
571 };
572 };
573 };
574 };
575 };
576 };
577 loop_.run();
578 loop_.close();
579 }
580 }
581
582 #[test]
583 fn file_test_mk_rm_dir() {
584 do run_in_bare_thread {
585 let mut loop_ = Loop::new();
586 let path = "./tmp/mk_rm_dir";
587 let mode = S_IWUSR |
588 S_IRUSR;
589 let mkdir_req = FsRequest::new();
590 do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| {
591 assert!(uverr.is_none());
592 let loop_ = req.get_loop();
593 let stat_req = FsRequest::new();
594 do stat_req.stat(&loop_, &path) |req, uverr| {
595 assert!(uverr.is_none());
596 let loop_ = req.get_loop();
597 let stat = req.get_stat();
598 naive_print(&loop_, format!("{:?}", stat));
599 assert!(stat.is_dir());
600 let rmdir_req = FsRequest::new();
601 do rmdir_req.rmdir(&loop_, &path) |req,uverr| {
602 assert!(uverr.is_none());
603 let loop_ = req.get_loop();
604 let stat_req = FsRequest::new();
605 do stat_req.stat(&loop_, &path) |_req, uverr| {
606 assert!(uverr.is_some());
607 }
608 }
609 }
610 }
611 loop_.run();
612 loop_.close();
613 }
614 }
615 #[test]
616 fn file_test_mkdir_chokes_on_double_create() {
617 do run_in_bare_thread {
618 let mut loop_ = Loop::new();
619 let path = "./tmp/double_create_dir";
620 let mode = S_IWUSR |
621 S_IRUSR;
622 let mkdir_req = FsRequest::new();
623 do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| {
624 assert!(uverr.is_none());
625 let loop_ = req.get_loop();
626 let mkdir_req = FsRequest::new();
627 do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| {
628 assert!(uverr.is_some());
629 let loop_ = req.get_loop();
630 let _stat = req.get_stat();
631 let rmdir_req = FsRequest::new();
632 do rmdir_req.rmdir(&loop_, &path) |req,uverr| {
633 assert!(uverr.is_none());
634 let _loop = req.get_loop();
635 }
636 }
637 }
638 loop_.run();
639 loop_.close();
640 }
641 }
642 #[test]
643 fn file_test_rmdir_chokes_on_nonexistant_path() {
644 do run_in_bare_thread {
645 let mut loop_ = Loop::new();
646 let path = "./tmp/never_existed_dir";
647 let rmdir_req = FsRequest::new();
648 do rmdir_req.rmdir(&loop_, &path) |_req, uverr| {
649 assert!(uverr.is_some());
650 }
651 loop_.run();
652 loop_.close();
653 }
654 }
655 }