(index<- ) ./librustuv/file.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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 std::c_str::CString;
12 use std::c_str;
13 use std::cast::transmute;
14 use std::cast;
15 use libc::{c_int, c_char, c_void, ssize_t};
16 use libc;
17 use std::rt::task::BlockedTask;
18 use std::io::{FileStat, IoError};
19 use std::io;
20 use std::rt::rtio;
21
22 use homing::{HomingIO, HomeHandle};
23 use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup};
24 use uvio::UvIoFactory;
25 use uvll;
26
27 pub struct FsRequest {
28 req: *uvll::uv_fs_t,
29 fired: bool,
30 }
31
32 pub struct FileWatcher {
33 loop_: Loop,
34 fd: c_int,
35 close: rtio::CloseBehavior,
36 home: HomeHandle,
37 }
38
39 impl FsRequest {
40 pub fn open(io: &mut UvIoFactory, path: &CString, flags: int, mode: int)
41 -> Result<FileWatcher, UvError>
42 {
43 execute(|req, cb| unsafe {
44 uvll::uv_fs_open(io.uv_loop(),
45 req, path.with_ref(|p| p), flags as c_int,
46 mode as c_int, cb)
47 }).map(|req|
48 FileWatcher::new(io, req.get_result() as c_int,
49 rtio::CloseSynchronously)
50 )
51 }
52
53 pub fn unlink(loop_: &Loop, path: &CString) -> Result<(), UvError> {
54 execute_nop(|req, cb| unsafe {
55 uvll::uv_fs_unlink(loop_.handle, req, path.with_ref(|p| p),
56 cb)
57 })
58 }
59
60 pub fn lstat(loop_: &Loop, path: &CString) -> Result<FileStat, UvError> {
61 execute(|req, cb| unsafe {
62 uvll::uv_fs_lstat(loop_.handle, req, path.with_ref(|p| p),
63 cb)
64 }).map(|req| req.mkstat())
65 }
66
67 pub fn stat(loop_: &Loop, path: &CString) -> Result<FileStat, UvError> {
68 execute(|req, cb| unsafe {
69 uvll::uv_fs_stat(loop_.handle, req, path.with_ref(|p| p),
70 cb)
71 }).map(|req| req.mkstat())
72 }
73
74 pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
75 -> Result<(), UvError>
76 {
77 // In libuv, uv_fs_write is basically just shelling out to a write()
78 // syscall at some point, with very little fluff around it. This means
79 // that write() could actually be a short write, so we need to be sure
80 // to call it continuously if we get a short write back. This method is
81 // expected to write the full data if it returns success.
82 let mut written = 0;
83 while written < buf.len() {
84 let offset = if offset == -1 {
85 offset
86 } else {
87 offset + written as i64
88 };
89 let uvbuf = uvll::uv_buf_t {
90 base: buf.slice_from(written as uint).as_ptr(),
91 len: (buf.len() - written) as uvll::uv_buf_len_t,
92 };
93 match execute(|req, cb| unsafe {
94 uvll::uv_fs_write(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
95 }).map(|req| req.get_result()) {
96 Err(e) => return Err(e),
97 Ok(n) => { written += n as uint; }
98 }
99 }
100 Ok(())
101 }
102
103 pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
104 -> Result<int, UvError>
105 {
106 execute(|req, cb| unsafe {
107 let uvbuf = uvll::uv_buf_t {
108 base: buf.as_ptr(),
109 len: buf.len() as uvll::uv_buf_len_t,
110 };
111 uvll::uv_fs_read(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
112 }).map(|req| {
113 req.get_result() as int
114 })
115 }
116
117 pub fn mkdir(loop_: &Loop, path: &CString, mode: c_int)
118 -> Result<(), UvError>
119 {
120 execute_nop(|req, cb| unsafe {
121 uvll::uv_fs_mkdir(loop_.handle, req, path.with_ref(|p| p),
122 mode, cb)
123 })
124 }
125
126 pub fn rmdir(loop_: &Loop, path: &CString) -> Result<(), UvError> {
127 execute_nop(|req, cb| unsafe {
128 uvll::uv_fs_rmdir(loop_.handle, req, path.with_ref(|p| p),
129 cb)
130 })
131 }
132
133 pub fn rename(loop_: &Loop, path: &CString, to: &CString)
134 -> Result<(), UvError>
135 {
136 execute_nop(|req, cb| unsafe {
137 uvll::uv_fs_rename(loop_.handle,
138 req,
139 path.with_ref(|p| p),
140 to.with_ref(|p| p),
141 cb)
142 })
143 }
144
145 pub fn chmod(loop_: &Loop, path: &CString, mode: c_int)
146 -> Result<(), UvError>
147 {
148 execute_nop(|req, cb| unsafe {
149 uvll::uv_fs_chmod(loop_.handle, req, path.with_ref(|p| p),
150 mode, cb)
151 })
152 }
153
154 pub fn readdir(loop_: &Loop, path: &CString, flags: c_int)
155 -> Result<Vec<Path>, UvError>
156 {
157 execute(|req, cb| unsafe {
158 uvll::uv_fs_readdir(loop_.handle,
159 req, path.with_ref(|p| p), flags, cb)
160 }).map(|req| unsafe {
161 let mut paths = vec!();
162 let path = CString::new(path.with_ref(|p| p), false);
163 let parent = Path::new(path);
164 let _ = c_str::from_c_multistring(req.get_ptr() as *libc::c_char,
165 Some(req.get_result() as uint),
166 |rel| {
167 let p = rel.as_bytes();
168 paths.push(parent.join(p.slice_to(rel.len())));
169 });
170 paths
171 })
172 }
173
174 pub fn readlink(loop_: &Loop, path: &CString) -> Result<Path, UvError> {
175 execute(|req, cb| unsafe {
176 uvll::uv_fs_readlink(loop_.handle, req,
177 path.with_ref(|p| p), cb)
178 }).map(|req| {
179 Path::new(unsafe {
180 CString::new(req.get_ptr() as *libc::c_char, false)
181 })
182 })
183 }
184
185 pub fn chown(loop_: &Loop, path: &CString, uid: int, gid: int)
186 -> Result<(), UvError>
187 {
188 execute_nop(|req, cb| unsafe {
189 uvll::uv_fs_chown(loop_.handle,
190 req, path.with_ref(|p| p),
191 uid as uvll::uv_uid_t,
192 gid as uvll::uv_gid_t,
193 cb)
194 })
195 }
196
197 pub fn truncate(loop_: &Loop, file: c_int, offset: i64)
198 -> Result<(), UvError>
199 {
200 execute_nop(|req, cb| unsafe {
201 uvll::uv_fs_ftruncate(loop_.handle, req, file, offset, cb)
202 })
203 }
204
205 pub fn link(loop_: &Loop, src: &CString, dst: &CString)
206 -> Result<(), UvError>
207 {
208 execute_nop(|req, cb| unsafe {
209 uvll::uv_fs_link(loop_.handle, req,
210 src.with_ref(|p| p),
211 dst.with_ref(|p| p),
212 cb)
213 })
214 }
215
216 pub fn symlink(loop_: &Loop, src: &CString, dst: &CString)
217 -> Result<(), UvError>
218 {
219 execute_nop(|req, cb| unsafe {
220 uvll::uv_fs_symlink(loop_.handle, req,
221 src.with_ref(|p| p),
222 dst.with_ref(|p| p),
223 0, cb)
224 })
225 }
226
227 pub fn fsync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
228 execute_nop(|req, cb| unsafe {
229 uvll::uv_fs_fsync(loop_.handle, req, fd, cb)
230 })
231 }
232
233 pub fn datasync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
234 execute_nop(|req, cb| unsafe {
235 uvll::uv_fs_fdatasync(loop_.handle, req, fd, cb)
236 })
237 }
238
239 pub fn utime(loop_: &Loop, path: &CString, atime: u64, mtime: u64)
240 -> Result<(), UvError>
241 {
242 // libuv takes seconds
243 let atime = atime as libc::c_double / 1000.0;
244 let mtime = mtime as libc::c_double / 1000.0;
245 execute_nop(|req, cb| unsafe {
246 uvll::uv_fs_utime(loop_.handle, req, path.with_ref(|p| p),
247 atime, mtime, cb)
248 })
249 }
250
251 pub fn get_result(&self) -> ssize_t {
252 unsafe { uvll::get_result_from_fs_req(self.req) }
253 }
254
255 pub fn get_stat(&self) -> uvll::uv_stat_t {
256 let stat = uvll::uv_stat_t::new();
257 unsafe { uvll::populate_stat(self.req, &stat); }
258 stat
259 }
260
261 pub fn get_ptr(&self) -> *libc::c_void {
262 unsafe { uvll::get_ptr_from_fs_req(self.req) }
263 }
264
265 pub fn mkstat(&self) -> FileStat {
266 let path = unsafe { uvll::get_path_from_fs_req(self.req) };
267 let path = unsafe { Path::new(CString::new(path, false)) };
268 let stat = self.get_stat();
269 fn to_msec(stat: uvll::uv_timespec_t) -> u64 {
270 // Be sure to cast to u64 first to prevent overflowing if the tv_sec
271 // field is a 32-bit integer.
272 (stat.tv_sec as u64) * 1000 + (stat.tv_nsec as u64) / 1000000
273 }
274 let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
275 libc::S_IFREG => io::TypeFile,
276 libc::S_IFDIR => io::TypeDirectory,
277 libc::S_IFIFO => io::TypeNamedPipe,
278 libc::S_IFBLK => io::TypeBlockSpecial,
279 libc::S_IFLNK => io::TypeSymlink,
280 _ => io::TypeUnknown,
281 };
282 FileStat {
283 path: path,
284 size: stat.st_size as u64,
285 kind: kind,
286 perm: unsafe {
287 io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
288 },
289 created: to_msec(stat.st_birthtim),
290 modified: to_msec(stat.st_mtim),
291 accessed: to_msec(stat.st_atim),
292 unstable: io::UnstableFileStat {
293 device: stat.st_dev as u64,
294 inode: stat.st_ino as u64,
295 rdev: stat.st_rdev as u64,
296 nlink: stat.st_nlink as u64,
297 uid: stat.st_uid as u64,
298 gid: stat.st_gid as u64,
299 blksize: stat.st_blksize as u64,
300 blocks: stat.st_blocks as u64,
301 flags: stat.st_flags as u64,
302 gen: stat.st_gen as u64,
303 }
304 }
305 }
306 }
307
308 impl Drop for FsRequest {
309 fn drop(&mut self) {
310 unsafe {
311 if self.fired {
312 uvll::uv_fs_req_cleanup(self.req);
313 }
314 uvll::free_req(self.req);
315 }
316 }
317 }
318
319 fn execute(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
320 -> Result<FsRequest, UvError>
321 {
322 let mut req = FsRequest {
323 fired: false,
324 req: unsafe { uvll::malloc_req(uvll::UV_FS) }
325 };
326 return match f(req.req, fs_cb) {
327 0 => {
328 req.fired = true;
329 let mut slot = None;
330 let loop_ = unsafe { uvll::get_loop_from_fs_req(req.req) };
331 wait_until_woken_after(&mut slot, &Loop::wrap(loop_), || {
332 unsafe { uvll::set_data_for_req(req.req, &slot) }
333 });
334 match req.get_result() {
335 n if n < 0 => Err(UvError(n as i32)),
336 _ => Ok(req),
337 }
338 }
339 n => Err(UvError(n))
340 };
341
342 extern fn fs_cb(req: *uvll::uv_fs_t) {
343 let slot: &mut Option<BlockedTask> = unsafe {
344 cast::transmute(uvll::get_data_for_req(req))
345 };
346 wakeup(slot);
347 }
348 }
349
350 fn execute_nop(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
351 -> Result<(), UvError> {
352 execute(f).map(|_| {})
353 }
354
355 impl HomingIO for FileWatcher {
356 fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
357 }
358
359 impl FileWatcher {
360 pub fn new(io: &mut UvIoFactory, fd: c_int,
361 close: rtio::CloseBehavior) -> FileWatcher {
362 FileWatcher {
363 loop_: Loop::wrap(io.uv_loop()),
364 fd: fd,
365 close: close,
366 home: io.make_handle(),
367 }
368 }
369
370 fn base_read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError> {
371 let _m = self.fire_homing_missile();
372 let r = FsRequest::read(&self.loop_, self.fd, buf, offset);
373 r.map_err(uv_error_to_io_error)
374 }
375 fn base_write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError> {
376 let _m = self.fire_homing_missile();
377 let r = FsRequest::write(&self.loop_, self.fd, buf, offset);
378 r.map_err(uv_error_to_io_error)
379 }
380 fn seek_common(&mut self, pos: i64, whence: c_int) ->
381 Result<u64, IoError>{
382 unsafe {
383 match libc::lseek(self.fd, pos as libc::off_t, whence) {
384 -1 => {
385 Err(IoError {
386 kind: io::OtherIoError,
387 desc: "Failed to lseek.",
388 detail: None
389 })
390 },
391 n => Ok(n as u64)
392 }
393 }
394 }
395 }
396
397 impl Drop for FileWatcher {
398 fn drop(&mut self) {
399 let _m = self.fire_homing_missile();
400 match self.close {
401 rtio::DontClose => {}
402 rtio::CloseAsynchronously => {
403 unsafe {
404 let req = uvll::malloc_req(uvll::UV_FS);
405 assert_eq!(uvll::uv_fs_close(self.loop_.handle, req,
406 self.fd, close_cb), 0);
407 }
408
409 extern fn close_cb(req: *uvll::uv_fs_t) {
410 unsafe {
411 uvll::uv_fs_req_cleanup(req);
412 uvll::free_req(req);
413 }
414 }
415 }
416 rtio::CloseSynchronously => {
417 let _ = execute_nop(|req, cb| unsafe {
418 uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
419 });
420 }
421 }
422 }
423 }
424
425 impl rtio::RtioFileStream for FileWatcher {
426 fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
427 self.base_read(buf, -1)
428 }
429 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
430 self.base_write(buf, -1)
431 }
432 fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
433 self.base_read(buf, offset as i64)
434 }
435 fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
436 self.base_write(buf, offset as i64)
437 }
438 fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
439 use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
440 let whence = match whence {
441 io::SeekSet => SEEK_SET,
442 io::SeekCur => SEEK_CUR,
443 io::SeekEnd => SEEK_END
444 };
445 self.seek_common(pos, whence)
446 }
447 fn tell(&self) -> Result<u64, IoError> {
448 use libc::SEEK_CUR;
449 // this is temporary
450 // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
451 let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
452 self_.seek_common(0, SEEK_CUR)
453 }
454 fn fsync(&mut self) -> Result<(), IoError> {
455 let _m = self.fire_homing_missile();
456 FsRequest::fsync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
457 }
458 fn datasync(&mut self) -> Result<(), IoError> {
459 let _m = self.fire_homing_missile();
460 FsRequest::datasync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
461 }
462 fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
463 let _m = self.fire_homing_missile();
464 let r = FsRequest::truncate(&self.loop_, self.fd, offset);
465 r.map_err(uv_error_to_io_error)
466 }
467 }
468
469 #[cfg(test)]
470 mod test {
471 use libc::c_int;
472 use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
473 use std::io;
474 use std::str;
475 use super::FsRequest;
476 use super::super::Loop;
477 use super::super::local_loop;
478
479 fn l() -> &mut Loop { &mut local_loop().loop_ }
480
481 #[test]
482 fn file_test_full_simple_sync() {
483 let create_flags = O_RDWR | O_CREAT;
484 let read_flags = O_RDONLY;
485 let mode = S_IWUSR | S_IRUSR;
486 let path_str = "./tmp/file_full_simple_sync.txt";
487
488 {
489 // open/create
490 let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
491 create_flags as int, mode as int);
492 assert!(result.is_ok());
493 let result = result.unwrap();
494 let fd = result.fd;
495
496 // write
497 let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
498 assert!(result.is_ok());
499 }
500
501 {
502 // re-open
503 let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
504 read_flags as int, 0);
505 assert!(result.is_ok());
506 let result = result.unwrap();
507 let fd = result.fd;
508
509 // read
510 let mut read_mem = Vec::from_elem(1000, 0u8);
511 let result = FsRequest::read(l(), fd, read_mem.as_mut_slice(), 0);
512 assert!(result.is_ok());
513
514 let nread = result.unwrap();
515 assert!(nread > 0);
516 let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
517 assert_eq!(read_str, "hello");
518 }
519 // unlink
520 let result = FsRequest::unlink(l(), &path_str.to_c_str());
521 assert!(result.is_ok());
522 }
523
524 #[test]
525 fn file_test_stat() {
526 let path = &"./tmp/file_test_stat_simple".to_c_str();
527 let create_flags = (O_RDWR | O_CREAT) as int;
528 let mode = (S_IWUSR | S_IRUSR) as int;
529
530 let result = FsRequest::open(local_loop(), path, create_flags, mode);
531 assert!(result.is_ok());
532 let file = result.unwrap();
533
534 let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
535 assert!(result.is_ok());
536
537 let result = FsRequest::stat(l(), path);
538 assert!(result.is_ok());
539 assert_eq!(result.unwrap().size, 5);
540
541 fn free<T>(_: T) {}
542 free(file);
543
544 let result = FsRequest::unlink(l(), path);
545 assert!(result.is_ok());
546 }
547
548 #[test]
549 fn file_test_mk_rm_dir() {
550 let path = &"./tmp/mk_rm_dir".to_c_str();
551 let mode = S_IWUSR | S_IRUSR;
552
553 let result = FsRequest::mkdir(l(), path, mode);
554 assert!(result.is_ok());
555
556 let result = FsRequest::stat(l(), path);
557 assert!(result.is_ok());
558 assert!(result.unwrap().kind == io::TypeDirectory);
559
560 let result = FsRequest::rmdir(l(), path);
561 assert!(result.is_ok());
562
563 let result = FsRequest::stat(l(), path);
564 assert!(result.is_err());
565 }
566
567 #[test]
568 fn file_test_mkdir_chokes_on_double_create() {
569 let path = &"./tmp/double_create_dir".to_c_str();
570 let mode = S_IWUSR | S_IRUSR;
571
572 let result = FsRequest::stat(l(), path);
573 assert!(result.is_err(), "{:?}", result);
574 let result = FsRequest::mkdir(l(), path, mode as c_int);
575 assert!(result.is_ok(), "{:?}", result);
576 let result = FsRequest::mkdir(l(), path, mode as c_int);
577 assert!(result.is_err(), "{:?}", result);
578 let result = FsRequest::rmdir(l(), path);
579 assert!(result.is_ok(), "{:?}", result);
580 }
581
582 #[test]
583 fn file_test_rmdir_chokes_on_nonexistant_path() {
584 let path = &"./tmp/never_existed_dir".to_c_str();
585 let result = FsRequest::rmdir(l(), path);
586 assert!(result.is_err());
587 }
588 }
librustuv/file.rs:31:1-31:1 -struct- definition:
pub struct FileWatcher {
loop_: Loop,
fd: c_int,
references:- 8361: close: rtio::CloseBehavior) -> FileWatcher {
362: FileWatcher {
363: loop_: Loop::wrap(io.uv_loop()),
--
397: impl Drop for FileWatcher {
398: fn drop(&mut self) {
--
425: impl rtio::RtioFileStream for FileWatcher {
426: fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
--
450: // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
451: let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
452: self_.seek_common(0, SEEK_CUR)
librustuv/file.rs:26:1-26:1 -struct- definition:
pub struct FsRequest {
req: *uvll::uv_fs_t,
fired: bool,
references:- 4321: {
322: let mut req = FsRequest {
323: fired: false,
librustuv/file.rs:349:1-349:1 -fn- definition:
fn execute_nop(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
-> Result<(), UvError> {
execute(f).map(|_| {})
references:- 13416: rtio::CloseSynchronously => {
417: let _ = execute_nop(|req, cb| unsafe {
418: uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
librustuv/file.rs:318:1-318:1 -fn- definition:
fn execute(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
-> Result<FsRequest, UvError>
{
references:- 8351: -> Result<(), UvError> {
352: execute(f).map(|_| {})
353: }
librustuv/file.rs:269:8-269:8 -fn- definition:
fn to_msec(stat: uvll::uv_timespec_t) -> u64 {
// Be sure to cast to u64 first to prevent overflowing if the tv_sec
// field is a 32-bit integer.
references:- 3288: },
289: created: to_msec(stat.st_birthtim),
290: modified: to_msec(stat.st_mtim),
291: accessed: to_msec(stat.st_atim),
292: unstable: io::UnstableFileStat {