2013-10-22 17:13:18 -05:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use std::c_str::CString;
|
2013-11-04 23:08:25 -06:00
|
|
|
use std::c_str;
|
2013-10-22 17:13:18 -05:00
|
|
|
use std::cast::transmute;
|
2013-11-04 23:08:25 -06:00
|
|
|
use std::cast;
|
2014-04-18 21:09:31 -05:00
|
|
|
use libc::{c_int, c_char, c_void, ssize_t};
|
2014-02-26 11:58:41 -06:00
|
|
|
use libc;
|
2013-12-12 19:47:48 -06:00
|
|
|
use std::rt::task::BlockedTask;
|
2013-11-11 00:46:32 -06:00
|
|
|
use std::io::{FileStat, IoError};
|
|
|
|
use std::io;
|
2013-11-07 17:13:06 -06:00
|
|
|
use std::rt::rtio;
|
2013-11-04 23:08:25 -06:00
|
|
|
|
2013-12-12 19:47:48 -06:00
|
|
|
use homing::{HomingIO, HomeHandle};
|
|
|
|
use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup};
|
|
|
|
use uvio::UvIoFactory;
|
2013-10-22 17:13:18 -05:00
|
|
|
use uvll;
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub struct FsRequest {
|
|
|
|
req: *uvll::uv_fs_t,
|
2014-03-28 12:27:14 -05:00
|
|
|
fired: bool,
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub struct FileWatcher {
|
2014-03-28 12:27:14 -05:00
|
|
|
loop_: Loop,
|
|
|
|
fd: c_int,
|
|
|
|
close: rtio::CloseBehavior,
|
|
|
|
home: HomeHandle,
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FsRequest {
|
2013-12-12 19:47:48 -06:00
|
|
|
pub fn open(io: &mut UvIoFactory, path: &CString, flags: int, mode: int)
|
2013-11-04 23:08:25 -06:00
|
|
|
-> Result<FileWatcher, UvError>
|
|
|
|
{
|
|
|
|
execute(|req, cb| unsafe {
|
2013-12-12 19:47:48 -06:00
|
|
|
uvll::uv_fs_open(io.uv_loop(),
|
2013-11-04 23:08:25 -06:00
|
|
|
req, path.with_ref(|p| p), flags as c_int,
|
|
|
|
mode as c_int, cb)
|
|
|
|
}).map(|req|
|
2013-12-12 19:47:48 -06:00
|
|
|
FileWatcher::new(io, req.get_result() as c_int,
|
2013-11-04 23:08:25 -06:00
|
|
|
rtio::CloseSynchronously)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unlink(loop_: &Loop, path: &CString) -> Result<(), UvError> {
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_unlink(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lstat(loop_: &Loop, path: &CString) -> Result<FileStat, UvError> {
|
|
|
|
execute(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_lstat(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
}).map(|req| req.mkstat())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stat(loop_: &Loop, path: &CString) -> Result<FileStat, UvError> {
|
|
|
|
execute(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_stat(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
}).map(|req| req.mkstat())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
2014-03-25 11:37:36 -05:00
|
|
|
// In libuv, uv_fs_write is basically just shelling out to a write()
|
|
|
|
// syscall at some point, with very little fluff around it. This means
|
|
|
|
// that write() could actually be a short write, so we need to be sure
|
|
|
|
// to call it continuously if we get a short write back. This method is
|
|
|
|
// expected to write the full data if it returns success.
|
|
|
|
let mut written = 0;
|
|
|
|
while written < buf.len() {
|
|
|
|
let offset = if offset == -1 {
|
|
|
|
offset
|
|
|
|
} else {
|
|
|
|
offset + written as i64
|
|
|
|
};
|
2014-04-18 21:09:31 -05:00
|
|
|
let uvbuf = uvll::uv_buf_t {
|
|
|
|
base: buf.slice_from(written as uint).as_ptr(),
|
|
|
|
len: (buf.len() - written) as uvll::uv_buf_len_t,
|
|
|
|
};
|
2014-03-25 11:37:36 -05:00
|
|
|
match execute(|req, cb| unsafe {
|
2014-04-18 21:09:31 -05:00
|
|
|
uvll::uv_fs_write(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
|
2014-03-25 11:37:36 -05:00
|
|
|
}).map(|req| req.get_result()) {
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
Ok(n) => { written += n as uint; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
|
|
|
|
-> Result<int, UvError>
|
|
|
|
{
|
2013-11-20 17:46:49 -06:00
|
|
|
execute(|req, cb| unsafe {
|
2014-04-18 21:09:31 -05:00
|
|
|
let uvbuf = uvll::uv_buf_t {
|
|
|
|
base: buf.as_ptr(),
|
|
|
|
len: buf.len() as uvll::uv_buf_len_t,
|
|
|
|
};
|
|
|
|
uvll::uv_fs_read(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
|
2013-11-20 17:46:49 -06:00
|
|
|
}).map(|req| {
|
2013-11-04 23:08:25 -06:00
|
|
|
req.get_result() as int
|
2013-11-20 17:46:49 -06:00
|
|
|
})
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn mkdir(loop_: &Loop, path: &CString, mode: c_int)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_mkdir(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
mode, cb)
|
|
|
|
})
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn rmdir(loop_: &Loop, path: &CString) -> Result<(), UvError> {
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_rmdir(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
})
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn rename(loop_: &Loop, path: &CString, to: &CString)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_rename(loop_.handle,
|
2013-11-04 23:08:25 -06:00
|
|
|
req,
|
2013-11-04 16:03:32 -06:00
|
|
|
path.with_ref(|p| p),
|
|
|
|
to.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
})
|
2013-10-25 18:50:08 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn chmod(loop_: &Loop, path: &CString, mode: c_int)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_chmod(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
mode, cb)
|
|
|
|
})
|
2013-10-25 19:04:37 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn readdir(loop_: &Loop, path: &CString, flags: c_int)
|
2014-04-08 20:45:20 -05:00
|
|
|
-> Result<Vec<Path>, UvError>
|
2013-11-04 23:08:25 -06:00
|
|
|
{
|
|
|
|
execute(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_readdir(loop_.handle,
|
2013-11-04 23:08:25 -06:00
|
|
|
req, path.with_ref(|p| p), flags, cb)
|
|
|
|
}).map(|req| unsafe {
|
2014-04-08 20:45:20 -05:00
|
|
|
let mut paths = vec!();
|
2013-11-04 23:08:25 -06:00
|
|
|
let path = CString::new(path.with_ref(|p| p), false);
|
2013-12-03 21:15:12 -06:00
|
|
|
let parent = Path::new(path);
|
2014-01-30 16:28:36 -06:00
|
|
|
let _ = c_str::from_c_multistring(req.get_ptr() as *libc::c_char,
|
|
|
|
Some(req.get_result() as uint),
|
|
|
|
|rel| {
|
2013-11-04 23:08:25 -06:00
|
|
|
let p = rel.as_bytes();
|
|
|
|
paths.push(parent.join(p.slice_to(rel.len())));
|
2013-11-20 17:46:49 -06:00
|
|
|
});
|
2013-11-04 23:08:25 -06:00
|
|
|
paths
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn readlink(loop_: &Loop, path: &CString) -> Result<Path, UvError> {
|
2013-11-20 17:46:49 -06:00
|
|
|
execute(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_readlink(loop_.handle, req,
|
2013-11-04 23:08:25 -06:00
|
|
|
path.with_ref(|p| p), cb)
|
2013-11-20 17:46:49 -06:00
|
|
|
}).map(|req| {
|
2013-12-03 21:15:12 -06:00
|
|
|
Path::new(unsafe {
|
2013-11-04 23:08:25 -06:00
|
|
|
CString::new(req.get_ptr() as *libc::c_char, false)
|
|
|
|
})
|
2013-11-20 17:46:49 -06:00
|
|
|
})
|
2013-10-30 01:31:07 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn chown(loop_: &Loop, path: &CString, uid: int, gid: int)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_chown(loop_.handle,
|
2013-11-04 23:08:25 -06:00
|
|
|
req, path.with_ref(|p| p),
|
2013-10-30 01:31:07 -05:00
|
|
|
uid as uvll::uv_uid_t,
|
|
|
|
gid as uvll::uv_gid_t,
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
})
|
2013-10-30 01:31:07 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn truncate(loop_: &Loop, file: c_int, offset: i64)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_ftruncate(loop_.handle, req, file, offset, cb)
|
2013-11-04 23:08:25 -06:00
|
|
|
})
|
2013-10-30 01:31:07 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn link(loop_: &Loop, src: &CString, dst: &CString)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_link(loop_.handle, req,
|
2013-10-30 01:31:07 -05:00
|
|
|
src.with_ref(|p| p),
|
|
|
|
dst.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
cb)
|
|
|
|
})
|
2013-10-30 01:31:07 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn symlink(loop_: &Loop, src: &CString, dst: &CString)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_symlink(loop_.handle, req,
|
2013-10-30 01:31:07 -05:00
|
|
|
src.with_ref(|p| p),
|
|
|
|
dst.with_ref(|p| p),
|
2013-11-04 23:08:25 -06:00
|
|
|
0, cb)
|
|
|
|
})
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn fsync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_fsync(loop_.handle, req, fd, cb)
|
2013-11-04 23:08:25 -06:00
|
|
|
})
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn datasync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
|
|
|
|
execute_nop(|req, cb| unsafe {
|
2013-11-05 13:29:45 -06:00
|
|
|
uvll::uv_fs_fdatasync(loop_.handle, req, fd, cb)
|
2013-11-04 23:08:25 -06:00
|
|
|
})
|
2013-10-30 01:31:07 -05:00
|
|
|
}
|
|
|
|
|
2013-11-05 17:48:27 -06:00
|
|
|
pub fn utime(loop_: &Loop, path: &CString, atime: u64, mtime: u64)
|
|
|
|
-> Result<(), UvError>
|
|
|
|
{
|
2013-11-06 01:29:11 -06:00
|
|
|
// libuv takes seconds
|
|
|
|
let atime = atime as libc::c_double / 1000.0;
|
|
|
|
let mtime = mtime as libc::c_double / 1000.0;
|
2013-11-05 17:48:27 -06:00
|
|
|
execute_nop(|req, cb| unsafe {
|
|
|
|
uvll::uv_fs_utime(loop_.handle, req, path.with_ref(|p| p),
|
2013-11-06 01:29:11 -06:00
|
|
|
atime, mtime, cb)
|
2013-11-05 17:48:27 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-03-25 11:37:36 -05:00
|
|
|
pub fn get_result(&self) -> ssize_t {
|
2013-11-04 23:08:25 -06:00
|
|
|
unsafe { uvll::get_result_from_fs_req(self.req) }
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn get_stat(&self) -> uvll::uv_stat_t {
|
|
|
|
let stat = uvll::uv_stat_t::new();
|
|
|
|
unsafe { uvll::populate_stat(self.req, &stat); }
|
2013-10-22 17:13:18 -05:00
|
|
|
stat
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_ptr(&self) -> *libc::c_void {
|
2013-11-04 23:08:25 -06:00
|
|
|
unsafe { uvll::get_ptr_from_fs_req(self.req) }
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
pub fn mkstat(&self) -> FileStat {
|
|
|
|
let path = unsafe { uvll::get_path_from_fs_req(self.req) };
|
2013-12-03 21:15:12 -06:00
|
|
|
let path = unsafe { Path::new(CString::new(path, false)) };
|
2013-11-04 23:08:25 -06:00
|
|
|
let stat = self.get_stat();
|
|
|
|
fn to_msec(stat: uvll::uv_timespec_t) -> u64 {
|
2013-11-05 17:30:42 -06:00
|
|
|
// Be sure to cast to u64 first to prevent overflowing if the tv_sec
|
|
|
|
// field is a 32-bit integer.
|
|
|
|
(stat.tv_sec as u64) * 1000 + (stat.tv_nsec as u64) / 1000000
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
|
|
|
let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
|
|
|
|
libc::S_IFREG => io::TypeFile,
|
|
|
|
libc::S_IFDIR => io::TypeDirectory,
|
|
|
|
libc::S_IFIFO => io::TypeNamedPipe,
|
|
|
|
libc::S_IFBLK => io::TypeBlockSpecial,
|
|
|
|
libc::S_IFLNK => io::TypeSymlink,
|
|
|
|
_ => io::TypeUnknown,
|
|
|
|
};
|
|
|
|
FileStat {
|
|
|
|
path: path,
|
|
|
|
size: stat.st_size as u64,
|
|
|
|
kind: kind,
|
2014-05-02 12:56:26 -05:00
|
|
|
perm: unsafe {
|
|
|
|
io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
|
|
|
|
},
|
2013-11-04 23:08:25 -06:00
|
|
|
created: to_msec(stat.st_birthtim),
|
|
|
|
modified: to_msec(stat.st_mtim),
|
|
|
|
accessed: to_msec(stat.st_atim),
|
|
|
|
unstable: io::UnstableFileStat {
|
|
|
|
device: stat.st_dev as u64,
|
|
|
|
inode: stat.st_ino as u64,
|
|
|
|
rdev: stat.st_rdev as u64,
|
|
|
|
nlink: stat.st_nlink as u64,
|
|
|
|
uid: stat.st_uid as u64,
|
|
|
|
gid: stat.st_gid as u64,
|
|
|
|
blksize: stat.st_blksize as u64,
|
|
|
|
blocks: stat.st_blocks as u64,
|
|
|
|
flags: stat.st_flags as u64,
|
|
|
|
gen: stat.st_gen as u64,
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
impl Drop for FsRequest {
|
|
|
|
fn drop(&mut self) {
|
2013-10-22 17:13:18 -05:00
|
|
|
unsafe {
|
2013-11-04 23:08:25 -06:00
|
|
|
if self.fired {
|
|
|
|
uvll::uv_fs_req_cleanup(self.req);
|
|
|
|
}
|
|
|
|
uvll::free_req(self.req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 20:15:10 -06:00
|
|
|
fn execute(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
|
2013-11-04 23:08:25 -06:00
|
|
|
-> Result<FsRequest, UvError>
|
|
|
|
{
|
2013-11-21 18:55:40 -06:00
|
|
|
let mut req = FsRequest {
|
|
|
|
fired: false,
|
|
|
|
req: unsafe { uvll::malloc_req(uvll::UV_FS) }
|
|
|
|
};
|
|
|
|
return match f(req.req, fs_cb) {
|
|
|
|
0 => {
|
|
|
|
req.fired = true;
|
|
|
|
let mut slot = None;
|
2014-02-10 21:59:35 -06:00
|
|
|
let loop_ = unsafe { uvll::get_loop_from_fs_req(req.req) };
|
|
|
|
wait_until_woken_after(&mut slot, &Loop::wrap(loop_), || {
|
2013-11-21 18:55:40 -06:00
|
|
|
unsafe { uvll::set_data_for_req(req.req, &slot) }
|
2013-11-20 17:46:49 -06:00
|
|
|
});
|
2013-11-21 18:55:40 -06:00
|
|
|
match req.get_result() {
|
2014-03-25 11:37:36 -05:00
|
|
|
n if n < 0 => Err(UvError(n as i32)),
|
2013-11-21 18:55:40 -06:00
|
|
|
_ => Ok(req),
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
2013-11-07 22:13:25 -06:00
|
|
|
}
|
2013-11-21 18:55:40 -06:00
|
|
|
n => Err(UvError(n))
|
2013-11-04 23:08:25 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
extern fn fs_cb(req: *uvll::uv_fs_t) {
|
|
|
|
let slot: &mut Option<BlockedTask> = unsafe {
|
|
|
|
cast::transmute(uvll::get_data_for_req(req))
|
|
|
|
};
|
2013-12-12 19:47:48 -06:00
|
|
|
wakeup(slot);
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 20:15:10 -06:00
|
|
|
fn execute_nop(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
|
|
|
|
-> Result<(), UvError> {
|
2013-11-04 23:08:25 -06:00
|
|
|
execute(f).map(|_| {})
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HomingIO for FileWatcher {
|
2013-12-12 19:47:48 -06:00
|
|
|
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FileWatcher {
|
2013-12-12 19:47:48 -06:00
|
|
|
pub fn new(io: &mut UvIoFactory, fd: c_int,
|
|
|
|
close: rtio::CloseBehavior) -> FileWatcher {
|
2013-11-04 23:08:25 -06:00
|
|
|
FileWatcher {
|
2013-12-12 19:47:48 -06:00
|
|
|
loop_: Loop::wrap(io.uv_loop()),
|
2013-11-04 23:08:25 -06:00
|
|
|
fd: fd,
|
|
|
|
close: close,
|
2013-12-12 19:47:48 -06:00
|
|
|
home: io.make_handle(),
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
2013-11-04 23:08:25 -06:00
|
|
|
|
|
|
|
fn base_read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError> {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
let r = FsRequest::read(&self.loop_, self.fd, buf, offset);
|
|
|
|
r.map_err(uv_error_to_io_error)
|
|
|
|
}
|
|
|
|
fn base_write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError> {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
let r = FsRequest::write(&self.loop_, self.fd, buf, offset);
|
|
|
|
r.map_err(uv_error_to_io_error)
|
|
|
|
}
|
|
|
|
fn seek_common(&mut self, pos: i64, whence: c_int) ->
|
|
|
|
Result<u64, IoError>{
|
|
|
|
unsafe {
|
|
|
|
match libc::lseek(self.fd, pos as libc::off_t, whence) {
|
|
|
|
-1 => {
|
|
|
|
Err(IoError {
|
|
|
|
kind: io::OtherIoError,
|
|
|
|
desc: "Failed to lseek.",
|
|
|
|
detail: None
|
|
|
|
})
|
|
|
|
},
|
|
|
|
n => Ok(n as u64)
|
|
|
|
}
|
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
impl Drop for FileWatcher {
|
|
|
|
fn drop(&mut self) {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
match self.close {
|
|
|
|
rtio::DontClose => {}
|
|
|
|
rtio::CloseAsynchronously => {
|
2013-11-06 01:29:11 -06:00
|
|
|
unsafe {
|
|
|
|
let req = uvll::malloc_req(uvll::UV_FS);
|
2014-01-30 16:28:36 -06:00
|
|
|
assert_eq!(uvll::uv_fs_close(self.loop_.handle, req,
|
|
|
|
self.fd, close_cb), 0);
|
2013-11-06 01:29:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
extern fn close_cb(req: *uvll::uv_fs_t) {
|
|
|
|
unsafe {
|
|
|
|
uvll::uv_fs_req_cleanup(req);
|
|
|
|
uvll::free_req(req);
|
|
|
|
}
|
|
|
|
}
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
|
|
|
rtio::CloseSynchronously => {
|
2014-01-23 11:53:05 -06:00
|
|
|
let _ = execute_nop(|req, cb| unsafe {
|
2013-11-06 01:29:11 -06:00
|
|
|
uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
|
|
|
|
});
|
2013-11-04 23:08:25 -06:00
|
|
|
}
|
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-04 23:08:25 -06:00
|
|
|
impl rtio::RtioFileStream for FileWatcher {
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
|
|
|
|
self.base_read(buf, -1)
|
|
|
|
}
|
|
|
|
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
|
|
|
|
self.base_write(buf, -1)
|
|
|
|
}
|
|
|
|
fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
|
|
|
|
self.base_read(buf, offset as i64)
|
|
|
|
}
|
|
|
|
fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
|
|
|
|
self.base_write(buf, offset as i64)
|
|
|
|
}
|
|
|
|
fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
|
2014-02-26 11:58:41 -06:00
|
|
|
use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
|
2013-11-04 23:08:25 -06:00
|
|
|
let whence = match whence {
|
|
|
|
io::SeekSet => SEEK_SET,
|
|
|
|
io::SeekCur => SEEK_CUR,
|
|
|
|
io::SeekEnd => SEEK_END
|
|
|
|
};
|
|
|
|
self.seek_common(pos, whence)
|
|
|
|
}
|
|
|
|
fn tell(&self) -> Result<u64, IoError> {
|
2014-02-26 11:58:41 -06:00
|
|
|
use libc::SEEK_CUR;
|
2013-11-04 23:08:25 -06:00
|
|
|
// this is temporary
|
std: deprecate cast::transmute_mut.
Turning a `&T` into an `&mut T` carries a large risk of undefined
behaviour, and needs to be done very very carefully. Providing a
convenience function for exactly this task is a bad idea, just tempting
people into doing the wrong thing.
The right thing is to use types like `Cell`, `RefCell` or `Unsafe`.
For memory safety, Rust has that guarantee that `&mut` pointers do not
alias with any other pointer, that is, if you have a `&mut T` then that
is the only usable pointer to that `T`. This allows Rust to assume that
writes through a `&mut T` do not affect the values of any other `&` or
`&mut` references. `&` pointers have no guarantees about aliasing or
not, so it's entirely possible for the same pointer to be passed into
both arguments of a function like
fn foo(x: &int, y: &int) { ... }
Converting either of `x` or `y` to a `&mut` pointer and modifying it
would affect the other value: invalid behaviour.
(Similarly, it's undefined behaviour to modify the value of an immutable
local, like `let x = 1;`.)
At a low-level, the *only* safe way to obtain an `&mut` out of a `&` is
using the `Unsafe` type (there are higher level wrappers around it, like
`Cell`, `RefCell`, `Mutex` etc.). The `Unsafe` type is registered with
the compiler so that it can reason a little about these `&` to `&mut`
casts, but it is still up to the user to ensure that the `&mut`s
obtained out of an `Unsafe` never alias.
(Note that *any* conversion from `&` to `&mut` can be invalid, including
a plain `transmute`, or casting `&T` -> `*T` -> `*mut T` -> `&mut T`.)
[breaking-change]
2014-05-04 08:17:37 -05:00
|
|
|
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
|
|
|
|
let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
|
2013-11-04 23:08:25 -06:00
|
|
|
self_.seek_common(0, SEEK_CUR)
|
|
|
|
}
|
|
|
|
fn fsync(&mut self) -> Result<(), IoError> {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
FsRequest::fsync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
|
|
|
|
}
|
|
|
|
fn datasync(&mut self) -> Result<(), IoError> {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
FsRequest::datasync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
|
|
|
|
}
|
|
|
|
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
|
2013-11-06 01:29:11 -06:00
|
|
|
let _m = self.fire_homing_missile();
|
2013-11-04 23:08:25 -06:00
|
|
|
let r = FsRequest::truncate(&self.loop_, self.fd, offset);
|
|
|
|
r.map_err(uv_error_to_io_error)
|
|
|
|
}
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2014-02-26 11:58:41 -06:00
|
|
|
use libc::c_int;
|
|
|
|
use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
|
2013-11-11 00:46:32 -06:00
|
|
|
use std::io;
|
2013-11-06 13:03:11 -06:00
|
|
|
use std::str;
|
2013-12-13 13:30:59 -06:00
|
|
|
use super::FsRequest;
|
|
|
|
use super::super::Loop;
|
|
|
|
use super::super::local_loop;
|
|
|
|
|
|
|
|
fn l() -> &mut Loop { &mut local_loop().loop_ }
|
2013-10-22 17:13:18 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn file_test_full_simple_sync() {
|
2013-11-07 17:13:06 -06:00
|
|
|
let create_flags = O_RDWR | O_CREAT;
|
|
|
|
let read_flags = O_RDONLY;
|
|
|
|
let mode = S_IWUSR | S_IRUSR;
|
|
|
|
let path_str = "./tmp/file_full_simple_sync.txt";
|
|
|
|
|
|
|
|
{
|
|
|
|
// open/create
|
2013-12-13 13:30:59 -06:00
|
|
|
let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
|
2013-11-07 17:13:06 -06:00
|
|
|
create_flags as int, mode as int);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
let result = result.unwrap();
|
|
|
|
let fd = result.fd;
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
// write
|
|
|
|
let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
|
2013-11-06 01:29:11 -06:00
|
|
|
assert!(result.is_ok());
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
2013-11-07 17:13:06 -06:00
|
|
|
|
|
|
|
{
|
|
|
|
// re-open
|
2013-12-13 13:30:59 -06:00
|
|
|
let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
|
2013-11-07 17:13:06 -06:00
|
|
|
read_flags as int, 0);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
let result = result.unwrap();
|
|
|
|
let fd = result.fd;
|
|
|
|
|
|
|
|
// read
|
2014-04-17 17:59:07 -05:00
|
|
|
let mut read_mem = Vec::from_elem(1000, 0u8);
|
|
|
|
let result = FsRequest::read(l(), fd, read_mem.as_mut_slice(), 0);
|
2013-11-07 17:13:06 -06:00
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
|
|
let nread = result.unwrap();
|
|
|
|
assert!(nread > 0);
|
2013-12-23 10:30:49 -06:00
|
|
|
let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
|
2013-11-28 06:52:11 -06:00
|
|
|
assert_eq!(read_str, "hello");
|
2013-11-07 17:13:06 -06:00
|
|
|
}
|
|
|
|
// unlink
|
|
|
|
let result = FsRequest::unlink(l(), &path_str.to_c_str());
|
|
|
|
assert!(result.is_ok());
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-11-06 13:03:11 -06:00
|
|
|
fn file_test_stat() {
|
2013-11-07 17:13:06 -06:00
|
|
|
let path = &"./tmp/file_test_stat_simple".to_c_str();
|
|
|
|
let create_flags = (O_RDWR | O_CREAT) as int;
|
|
|
|
let mode = (S_IWUSR | S_IRUSR) as int;
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-12-13 13:30:59 -06:00
|
|
|
let result = FsRequest::open(local_loop(), path, create_flags, mode);
|
2013-11-07 17:13:06 -06:00
|
|
|
assert!(result.is_ok());
|
|
|
|
let file = result.unwrap();
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
|
|
|
|
assert!(result.is_ok());
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::stat(l(), path);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
assert_eq!(result.unwrap().size, 5);
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
fn free<T>(_: T) {}
|
|
|
|
free(file);
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::unlink(l(), path);
|
|
|
|
assert!(result.is_ok());
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn file_test_mk_rm_dir() {
|
2013-11-07 17:13:06 -06:00
|
|
|
let path = &"./tmp/mk_rm_dir".to_c_str();
|
|
|
|
let mode = S_IWUSR | S_IRUSR;
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::mkdir(l(), path, mode);
|
|
|
|
assert!(result.is_ok());
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::stat(l(), path);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
assert!(result.unwrap().kind == io::TypeDirectory);
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::rmdir(l(), path);
|
|
|
|
assert!(result.is_ok());
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-11-07 17:13:06 -06:00
|
|
|
let result = FsRequest::stat(l(), path);
|
|
|
|
assert!(result.is_err());
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-10-22 17:13:18 -05:00
|
|
|
#[test]
|
|
|
|
fn file_test_mkdir_chokes_on_double_create() {
|
2013-11-07 17:13:06 -06:00
|
|
|
let path = &"./tmp/double_create_dir".to_c_str();
|
|
|
|
let mode = S_IWUSR | S_IRUSR;
|
|
|
|
|
|
|
|
let result = FsRequest::stat(l(), path);
|
|
|
|
assert!(result.is_err(), "{:?}", result);
|
|
|
|
let result = FsRequest::mkdir(l(), path, mode as c_int);
|
|
|
|
assert!(result.is_ok(), "{:?}", result);
|
|
|
|
let result = FsRequest::mkdir(l(), path, mode as c_int);
|
|
|
|
assert!(result.is_err(), "{:?}", result);
|
|
|
|
let result = FsRequest::rmdir(l(), path);
|
|
|
|
assert!(result.is_ok(), "{:?}", result);
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
2013-11-06 13:03:11 -06:00
|
|
|
|
2013-10-22 17:13:18 -05:00
|
|
|
#[test]
|
|
|
|
fn file_test_rmdir_chokes_on_nonexistant_path() {
|
2013-11-07 17:13:06 -06:00
|
|
|
let path = &"./tmp/never_existed_dir".to_c_str();
|
|
|
|
let result = FsRequest::rmdir(l(), path);
|
|
|
|
assert!(result.is_err());
|
2013-10-22 17:13:18 -05:00
|
|
|
}
|
|
|
|
}
|