]> git.lizzy.rs Git - rust.git/blob - src/librustuv/file.rs
Ignore tests broken by failing on ICE
[rust.git] / src / librustuv / 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 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: (stat.st_mode as io::FilePermission) & io::AllPermissions,
287             created: to_msec(stat.st_birthtim),
288             modified: to_msec(stat.st_mtim),
289             accessed: to_msec(stat.st_atim),
290             unstable: io::UnstableFileStat {
291                 device: stat.st_dev as u64,
292                 inode: stat.st_ino as u64,
293                 rdev: stat.st_rdev as u64,
294                 nlink: stat.st_nlink as u64,
295                 uid: stat.st_uid as u64,
296                 gid: stat.st_gid as u64,
297                 blksize: stat.st_blksize as u64,
298                 blocks: stat.st_blocks as u64,
299                 flags: stat.st_flags as u64,
300                 gen: stat.st_gen as u64,
301             }
302         }
303     }
304 }
305
306 impl Drop for FsRequest {
307     fn drop(&mut self) {
308         unsafe {
309             if self.fired {
310                 uvll::uv_fs_req_cleanup(self.req);
311             }
312             uvll::free_req(self.req);
313         }
314     }
315 }
316
317 fn execute(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
318     -> Result<FsRequest, UvError>
319 {
320     let mut req = FsRequest {
321         fired: false,
322         req: unsafe { uvll::malloc_req(uvll::UV_FS) }
323     };
324     return match f(req.req, fs_cb) {
325         0 => {
326             req.fired = true;
327             let mut slot = None;
328             let loop_ = unsafe { uvll::get_loop_from_fs_req(req.req) };
329             wait_until_woken_after(&mut slot, &Loop::wrap(loop_), || {
330                 unsafe { uvll::set_data_for_req(req.req, &slot) }
331             });
332             match req.get_result() {
333                 n if n < 0 => Err(UvError(n as i32)),
334                 _ => Ok(req),
335             }
336         }
337         n => Err(UvError(n))
338     };
339
340     extern fn fs_cb(req: *uvll::uv_fs_t) {
341         let slot: &mut Option<BlockedTask> = unsafe {
342             cast::transmute(uvll::get_data_for_req(req))
343         };
344         wakeup(slot);
345     }
346 }
347
348 fn execute_nop(f: |*uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
349     -> Result<(), UvError> {
350     execute(f).map(|_| {})
351 }
352
353 impl HomingIO for FileWatcher {
354     fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
355 }
356
357 impl FileWatcher {
358     pub fn new(io: &mut UvIoFactory, fd: c_int,
359                close: rtio::CloseBehavior) -> FileWatcher {
360         FileWatcher {
361             loop_: Loop::wrap(io.uv_loop()),
362             fd: fd,
363             close: close,
364             home: io.make_handle(),
365         }
366     }
367
368     fn base_read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError> {
369         let _m = self.fire_homing_missile();
370         let r = FsRequest::read(&self.loop_, self.fd, buf, offset);
371         r.map_err(uv_error_to_io_error)
372     }
373     fn base_write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError> {
374         let _m = self.fire_homing_missile();
375         let r = FsRequest::write(&self.loop_, self.fd, buf, offset);
376         r.map_err(uv_error_to_io_error)
377     }
378     fn seek_common(&mut self, pos: i64, whence: c_int) ->
379         Result<u64, IoError>{
380         unsafe {
381             match libc::lseek(self.fd, pos as libc::off_t, whence) {
382                 -1 => {
383                     Err(IoError {
384                         kind: io::OtherIoError,
385                         desc: "Failed to lseek.",
386                         detail: None
387                     })
388                 },
389                 n => Ok(n as u64)
390             }
391         }
392     }
393 }
394
395 impl Drop for FileWatcher {
396     fn drop(&mut self) {
397         let _m = self.fire_homing_missile();
398         match self.close {
399             rtio::DontClose => {}
400             rtio::CloseAsynchronously => {
401                 unsafe {
402                     let req = uvll::malloc_req(uvll::UV_FS);
403                     assert_eq!(uvll::uv_fs_close(self.loop_.handle, req,
404                                                  self.fd, close_cb), 0);
405                 }
406
407                 extern fn close_cb(req: *uvll::uv_fs_t) {
408                     unsafe {
409                         uvll::uv_fs_req_cleanup(req);
410                         uvll::free_req(req);
411                     }
412                 }
413             }
414             rtio::CloseSynchronously => {
415                 let _ = execute_nop(|req, cb| unsafe {
416                     uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
417                 });
418             }
419         }
420     }
421 }
422
423 impl rtio::RtioFileStream for FileWatcher {
424     fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
425         self.base_read(buf, -1)
426     }
427     fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
428         self.base_write(buf, -1)
429     }
430     fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
431         self.base_read(buf, offset as i64)
432     }
433     fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
434         self.base_write(buf, offset as i64)
435     }
436     fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
437         use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
438         let whence = match whence {
439             io::SeekSet => SEEK_SET,
440             io::SeekCur => SEEK_CUR,
441             io::SeekEnd => SEEK_END
442         };
443         self.seek_common(pos, whence)
444     }
445     fn tell(&self) -> Result<u64, IoError> {
446         use libc::SEEK_CUR;
447         // this is temporary
448         // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
449         let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
450         self_.seek_common(0, SEEK_CUR)
451     }
452     fn fsync(&mut self) -> Result<(), IoError> {
453         let _m = self.fire_homing_missile();
454         FsRequest::fsync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
455     }
456     fn datasync(&mut self) -> Result<(), IoError> {
457         let _m = self.fire_homing_missile();
458         FsRequest::datasync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
459     }
460     fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
461         let _m = self.fire_homing_missile();
462         let r = FsRequest::truncate(&self.loop_, self.fd, offset);
463         r.map_err(uv_error_to_io_error)
464     }
465 }
466
467 #[cfg(test)]
468 mod test {
469     use libc::c_int;
470     use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
471     use std::io;
472     use std::str;
473     use super::FsRequest;
474     use super::super::Loop;
475     use super::super::local_loop;
476
477     fn l() -> &mut Loop { &mut local_loop().loop_ }
478
479     #[test]
480     fn file_test_full_simple_sync() {
481         let create_flags = O_RDWR | O_CREAT;
482         let read_flags = O_RDONLY;
483         let mode = S_IWUSR | S_IRUSR;
484         let path_str = "./tmp/file_full_simple_sync.txt";
485
486         {
487             // open/create
488             let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
489                                          create_flags as int, mode as int);
490             assert!(result.is_ok());
491             let result = result.unwrap();
492             let fd = result.fd;
493
494             // write
495             let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
496             assert!(result.is_ok());
497         }
498
499         {
500             // re-open
501             let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
502                                          read_flags as int, 0);
503             assert!(result.is_ok());
504             let result = result.unwrap();
505             let fd = result.fd;
506
507             // read
508             let mut read_mem = Vec::from_elem(1000, 0u8);
509             let result = FsRequest::read(l(), fd, read_mem.as_mut_slice(), 0);
510             assert!(result.is_ok());
511
512             let nread = result.unwrap();
513             assert!(nread > 0);
514             let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
515             assert_eq!(read_str, "hello");
516         }
517         // unlink
518         let result = FsRequest::unlink(l(), &path_str.to_c_str());
519         assert!(result.is_ok());
520     }
521
522     #[test]
523     fn file_test_stat() {
524         let path = &"./tmp/file_test_stat_simple".to_c_str();
525         let create_flags = (O_RDWR | O_CREAT) as int;
526         let mode = (S_IWUSR | S_IRUSR) as int;
527
528         let result = FsRequest::open(local_loop(), path, create_flags, mode);
529         assert!(result.is_ok());
530         let file = result.unwrap();
531
532         let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
533         assert!(result.is_ok());
534
535         let result = FsRequest::stat(l(), path);
536         assert!(result.is_ok());
537         assert_eq!(result.unwrap().size, 5);
538
539         fn free<T>(_: T) {}
540         free(file);
541
542         let result = FsRequest::unlink(l(), path);
543         assert!(result.is_ok());
544     }
545
546     #[test]
547     fn file_test_mk_rm_dir() {
548         let path = &"./tmp/mk_rm_dir".to_c_str();
549         let mode = S_IWUSR | S_IRUSR;
550
551         let result = FsRequest::mkdir(l(), path, mode);
552         assert!(result.is_ok());
553
554         let result = FsRequest::stat(l(), path);
555         assert!(result.is_ok());
556         assert!(result.unwrap().kind == io::TypeDirectory);
557
558         let result = FsRequest::rmdir(l(), path);
559         assert!(result.is_ok());
560
561         let result = FsRequest::stat(l(), path);
562         assert!(result.is_err());
563     }
564
565     #[test]
566     fn file_test_mkdir_chokes_on_double_create() {
567         let path = &"./tmp/double_create_dir".to_c_str();
568         let mode = S_IWUSR | S_IRUSR;
569
570         let result = FsRequest::stat(l(), path);
571         assert!(result.is_err(), "{:?}", result);
572         let result = FsRequest::mkdir(l(), path, mode as c_int);
573         assert!(result.is_ok(), "{:?}", result);
574         let result = FsRequest::mkdir(l(), path, mode as c_int);
575         assert!(result.is_err(), "{:?}", result);
576         let result = FsRequest::rmdir(l(), path);
577         assert!(result.is_ok(), "{:?}", result);
578     }
579
580     #[test]
581     fn file_test_rmdir_chokes_on_nonexistant_path() {
582         let path = &"./tmp/never_existed_dir".to_c_str();
583         let result = FsRequest::rmdir(l(), path);
584         assert!(result.is_err());
585     }
586 }