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.
11 use libc::{c_int, c_char, c_void, ssize_t};
13 use std::c_str::CString;
17 use std::rt::rtio::{IoResult, IoError};
19 use std::rt::task::BlockedTask;
21 use homing::{HomingIO, HomeHandle};
22 use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup};
23 use uvio::UvIoFactory;
26 pub struct FsRequest {
27 req: *mut uvll::uv_fs_t,
31 pub struct FileWatcher {
34 close: rtio::CloseBehavior,
39 pub fn open(io: &mut UvIoFactory, path: &CString, flags: int, mode: int)
40 -> Result<FileWatcher, UvError>
42 execute(|req, cb| unsafe {
43 uvll::uv_fs_open(io.uv_loop(),
44 req, path.as_ptr(), flags as c_int,
47 FileWatcher::new(io, req.get_result() as c_int,
48 rtio::CloseSynchronously)
52 pub fn unlink(loop_: &Loop, path: &CString) -> Result<(), UvError> {
53 execute_nop(|req, cb| unsafe {
54 uvll::uv_fs_unlink(loop_.handle, req, path.as_ptr(),
59 pub fn lstat(loop_: &Loop, path: &CString)
60 -> Result<rtio::FileStat, UvError>
62 execute(|req, cb| unsafe {
63 uvll::uv_fs_lstat(loop_.handle, req, path.as_ptr(),
65 }).map(|req| req.mkstat())
68 pub fn stat(loop_: &Loop, path: &CString) -> Result<rtio::FileStat, UvError> {
69 execute(|req, cb| unsafe {
70 uvll::uv_fs_stat(loop_.handle, req, path.as_ptr(),
72 }).map(|req| req.mkstat())
75 pub fn fstat(loop_: &Loop, fd: c_int) -> Result<rtio::FileStat, UvError> {
76 execute(|req, cb| unsafe {
77 uvll::uv_fs_fstat(loop_.handle, req, fd, cb)
78 }).map(|req| req.mkstat())
81 pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
82 -> Result<(), UvError>
84 // In libuv, uv_fs_write is basically just shelling out to a write()
85 // syscall at some point, with very little fluff around it. This means
86 // that write() could actually be a short write, so we need to be sure
87 // to call it continuously if we get a short write back. This method is
88 // expected to write the full data if it returns success.
90 while written < buf.len() {
91 let offset = if offset == -1 {
94 offset + written as i64
96 let uvbuf = uvll::uv_buf_t {
97 base: buf.slice_from(written as uint).as_ptr() as *mut _,
98 len: (buf.len() - written) as uvll::uv_buf_len_t,
100 match execute(|req, cb| unsafe {
101 uvll::uv_fs_write(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
102 }).map(|req| req.get_result()) {
103 Err(e) => return Err(e),
104 Ok(n) => { written += n as uint; }
110 pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
111 -> Result<int, UvError>
113 execute(|req, cb| unsafe {
114 let mut uvbuf = uvll::uv_buf_t {
115 base: buf.as_mut_ptr(),
116 len: buf.len() as uvll::uv_buf_len_t,
118 uvll::uv_fs_read(loop_.handle, req, fd, &mut uvbuf, 1, offset, cb)
120 req.get_result() as int
124 pub fn mkdir(loop_: &Loop, path: &CString, mode: c_int)
125 -> Result<(), UvError>
127 execute_nop(|req, cb| unsafe {
128 uvll::uv_fs_mkdir(loop_.handle, req, path.as_ptr(),
133 pub fn rmdir(loop_: &Loop, path: &CString) -> Result<(), UvError> {
134 execute_nop(|req, cb| unsafe {
135 uvll::uv_fs_rmdir(loop_.handle, req, path.as_ptr(),
140 pub fn rename(loop_: &Loop, path: &CString, to: &CString)
141 -> Result<(), UvError>
143 execute_nop(|req, cb| unsafe {
144 uvll::uv_fs_rename(loop_.handle,
152 pub fn chmod(loop_: &Loop, path: &CString, mode: c_int)
153 -> Result<(), UvError>
155 execute_nop(|req, cb| unsafe {
156 uvll::uv_fs_chmod(loop_.handle, req, path.as_ptr(),
161 pub fn readdir(loop_: &Loop, path: &CString, flags: c_int)
162 -> Result<Vec<CString>, UvError>
164 execute(|req, cb| unsafe {
165 uvll::uv_fs_readdir(loop_.handle,
166 req, path.as_ptr(), flags, cb)
167 }).map(|req| unsafe {
168 let mut paths = vec!();
169 let path = CString::new(path.as_ptr(), false);
170 let parent = Path::new(path);
171 let _ = c_str::from_c_multistring(req.get_ptr() as *const libc::c_char,
172 Some(req.get_result() as uint),
174 let p = rel.as_bytes();
175 paths.push(parent.join(p.slice_to(rel.len())).to_c_str());
181 pub fn readlink(loop_: &Loop, path: &CString) -> Result<CString, UvError> {
182 execute(|req, cb| unsafe {
183 uvll::uv_fs_readlink(loop_.handle, req,
186 // Be sure to clone the cstring so we get an independently owned
187 // allocation to work with and return.
189 CString::new(req.get_ptr() as *const libc::c_char, false).clone()
194 pub fn chown(loop_: &Loop, path: &CString, uid: int, gid: int)
195 -> Result<(), UvError>
197 execute_nop(|req, cb| unsafe {
198 uvll::uv_fs_chown(loop_.handle,
200 uid as uvll::uv_uid_t,
201 gid as uvll::uv_gid_t,
206 pub fn truncate(loop_: &Loop, file: c_int, offset: i64)
207 -> Result<(), UvError>
209 execute_nop(|req, cb| unsafe {
210 uvll::uv_fs_ftruncate(loop_.handle, req, file, offset, cb)
214 pub fn link(loop_: &Loop, src: &CString, dst: &CString)
215 -> Result<(), UvError>
217 execute_nop(|req, cb| unsafe {
218 uvll::uv_fs_link(loop_.handle, req,
225 pub fn symlink(loop_: &Loop, src: &CString, dst: &CString)
226 -> Result<(), UvError>
228 execute_nop(|req, cb| unsafe {
229 uvll::uv_fs_symlink(loop_.handle, req,
236 pub fn fsync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
237 execute_nop(|req, cb| unsafe {
238 uvll::uv_fs_fsync(loop_.handle, req, fd, cb)
242 pub fn datasync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
243 execute_nop(|req, cb| unsafe {
244 uvll::uv_fs_fdatasync(loop_.handle, req, fd, cb)
248 pub fn utime(loop_: &Loop, path: &CString, atime: u64, mtime: u64)
249 -> Result<(), UvError>
251 // libuv takes seconds
252 let atime = atime as libc::c_double / 1000.0;
253 let mtime = mtime as libc::c_double / 1000.0;
254 execute_nop(|req, cb| unsafe {
255 uvll::uv_fs_utime(loop_.handle, req, path.as_ptr(),
260 pub fn get_result(&self) -> ssize_t {
261 unsafe { uvll::get_result_from_fs_req(self.req) }
264 pub fn get_stat(&self) -> uvll::uv_stat_t {
265 let mut stat = uvll::uv_stat_t::new();
266 unsafe { uvll::populate_stat(self.req, &mut stat); }
270 pub fn get_ptr(&self) -> *mut libc::c_void {
271 unsafe { uvll::get_ptr_from_fs_req(self.req) }
274 pub fn mkstat(&self) -> rtio::FileStat {
275 let stat = self.get_stat();
276 fn to_msec(stat: uvll::uv_timespec_t) -> u64 {
277 // Be sure to cast to u64 first to prevent overflowing if the tv_sec
278 // field is a 32-bit integer.
279 (stat.tv_sec as u64) * 1000 + (stat.tv_nsec as u64) / 1000000
282 size: stat.st_size as u64,
283 kind: stat.st_mode as u64,
284 perm: stat.st_mode as u64,
285 created: to_msec(stat.st_birthtim),
286 modified: to_msec(stat.st_mtim),
287 accessed: to_msec(stat.st_atim),
288 device: stat.st_dev as u64,
289 inode: stat.st_ino as u64,
290 rdev: stat.st_rdev as u64,
291 nlink: stat.st_nlink as u64,
292 uid: stat.st_uid as u64,
293 gid: stat.st_gid as u64,
294 blksize: stat.st_blksize as u64,
295 blocks: stat.st_blocks as u64,
296 flags: stat.st_flags as u64,
297 gen: stat.st_gen as u64,
302 impl Drop for FsRequest {
306 uvll::uv_fs_req_cleanup(self.req);
308 uvll::free_req(self.req);
313 fn execute(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
314 -> Result<FsRequest, UvError>
316 let mut req = FsRequest {
318 req: unsafe { uvll::malloc_req(uvll::UV_FS) }
320 return match f(req.req, fs_cb) {
324 let loop_ = unsafe { uvll::get_loop_from_fs_req(req.req) };
325 wait_until_woken_after(&mut slot, &Loop::wrap(loop_), || {
326 unsafe { uvll::set_data_for_req(req.req, &mut slot) }
328 match req.get_result() {
329 n if n < 0 => Err(UvError(n as i32)),
336 extern fn fs_cb(req: *mut uvll::uv_fs_t) {
337 let slot: &mut Option<BlockedTask> = unsafe {
338 mem::transmute(uvll::get_data_for_req(req))
344 fn execute_nop(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
345 -> Result<(), UvError> {
346 execute(f).map(|_| {})
349 impl HomingIO for FileWatcher {
350 fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
354 pub fn new(io: &mut UvIoFactory, fd: c_int,
355 close: rtio::CloseBehavior) -> FileWatcher {
357 loop_: Loop::wrap(io.uv_loop()),
360 home: io.make_handle(),
364 fn base_read(&mut self, buf: &mut [u8], offset: i64) -> IoResult<int> {
365 let _m = self.fire_homing_missile();
366 let r = FsRequest::read(&self.loop_, self.fd, buf, offset);
367 r.map_err(uv_error_to_io_error)
369 fn base_write(&mut self, buf: &[u8], offset: i64) -> IoResult<()> {
370 let _m = self.fire_homing_missile();
371 let r = FsRequest::write(&self.loop_, self.fd, buf, offset);
372 r.map_err(uv_error_to_io_error)
374 fn seek_common(&self, pos: i64, whence: c_int) -> IoResult<u64>{
375 match unsafe { libc::lseek(self.fd, pos as libc::off_t, whence) } {
378 code: os::errno() as uint,
388 impl Drop for FileWatcher {
390 let _m = self.fire_homing_missile();
392 rtio::DontClose => {}
393 rtio::CloseAsynchronously => {
395 let req = uvll::malloc_req(uvll::UV_FS);
396 assert_eq!(uvll::uv_fs_close(self.loop_.handle, req,
397 self.fd, close_cb), 0);
400 extern fn close_cb(req: *mut uvll::uv_fs_t) {
402 uvll::uv_fs_req_cleanup(req);
407 rtio::CloseSynchronously => {
408 let _ = execute_nop(|req, cb| unsafe {
409 uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
416 impl rtio::RtioFileStream for FileWatcher {
417 fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
418 self.base_read(buf, -1)
420 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
421 self.base_write(buf, -1)
423 fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
424 self.base_read(buf, offset as i64)
426 fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
427 self.base_write(buf, offset as i64)
429 fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult<u64> {
430 use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
431 let whence = match whence {
432 rtio::SeekSet => SEEK_SET,
433 rtio::SeekCur => SEEK_CUR,
434 rtio::SeekEnd => SEEK_END
436 self.seek_common(pos, whence)
438 fn tell(&self) -> IoResult<u64> {
441 self.seek_common(0, SEEK_CUR)
443 fn fsync(&mut self) -> IoResult<()> {
444 let _m = self.fire_homing_missile();
445 FsRequest::fsync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
447 fn datasync(&mut self) -> IoResult<()> {
448 let _m = self.fire_homing_missile();
449 FsRequest::datasync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
451 fn truncate(&mut self, offset: i64) -> IoResult<()> {
452 let _m = self.fire_homing_missile();
453 let r = FsRequest::truncate(&self.loop_, self.fd, offset);
454 r.map_err(uv_error_to_io_error)
457 fn fstat(&mut self) -> IoResult<rtio::FileStat> {
458 let _m = self.fire_homing_missile();
459 FsRequest::fstat(&self.loop_, self.fd).map_err(uv_error_to_io_error)
466 use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
468 use super::FsRequest;
469 use super::super::Loop;
470 use super::super::local_loop;
472 fn l() -> &mut Loop { &mut local_loop().loop_ }
475 fn file_test_full_simple_sync() {
476 let create_flags = O_RDWR | O_CREAT;
477 let read_flags = O_RDONLY;
478 let mode = S_IWUSR | S_IRUSR;
479 let path_str = "./tmp/file_full_simple_sync.txt";
483 let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
484 create_flags as int, mode as int);
485 assert!(result.is_ok());
486 let result = result.unwrap();
490 let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
491 assert!(result.is_ok());
496 let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
497 read_flags as int, 0);
498 assert!(result.is_ok());
499 let result = result.unwrap();
503 let mut read_mem = Vec::from_elem(1000, 0u8);
504 let result = FsRequest::read(l(), fd, read_mem.as_mut_slice(), 0);
505 assert!(result.is_ok());
507 let nread = result.unwrap();
509 let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
510 assert_eq!(read_str, "hello");
513 let result = FsRequest::unlink(l(), &path_str.to_c_str());
514 assert!(result.is_ok());
518 fn file_test_stat() {
519 let path = &"./tmp/file_test_stat_simple".to_c_str();
520 let create_flags = (O_RDWR | O_CREAT) as int;
521 let mode = (S_IWUSR | S_IRUSR) as int;
523 let result = FsRequest::open(local_loop(), path, create_flags, mode);
524 assert!(result.is_ok());
525 let file = result.unwrap();
527 let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
528 assert!(result.is_ok());
530 let result = FsRequest::stat(l(), path);
531 assert!(result.is_ok());
532 assert_eq!(result.unwrap().size, 5);
534 let result = FsRequest::fstat(l(), file.fd);
535 assert!(result.is_ok());
536 assert_eq!(result.unwrap().size, 5);
541 let result = FsRequest::unlink(l(), path);
542 assert!(result.is_ok());
546 fn file_test_mk_rm_dir() {
547 let path = &"./tmp/mk_rm_dir".to_c_str();
548 let mode = S_IWUSR | S_IRUSR;
550 let result = FsRequest::mkdir(l(), path, mode as c_int);
551 assert!(result.is_ok());
553 let result = FsRequest::rmdir(l(), path);
554 assert!(result.is_ok());
556 let result = FsRequest::stat(l(), path);
557 assert!(result.is_err());
561 fn file_test_mkdir_chokes_on_double_create() {
562 let path = &"./tmp/double_create_dir".to_c_str();
563 let mode = S_IWUSR | S_IRUSR;
565 let result = FsRequest::stat(l(), path);
566 assert!(result.is_err(), "{:?}", result);
567 let result = FsRequest::mkdir(l(), path, mode as c_int);
568 assert!(result.is_ok(), "{:?}", result);
569 let result = FsRequest::mkdir(l(), path, mode as c_int);
570 assert!(result.is_err(), "{:?}", result);
571 let result = FsRequest::rmdir(l(), path);
572 assert!(result.is_ok(), "{:?}", result);
576 fn file_test_rmdir_chokes_on_nonexistant_path() {
577 let path = &"./tmp/never_existed_dir".to_c_str();
578 let result = FsRequest::rmdir(l(), path);
579 assert!(result.is_err());