]> git.lizzy.rs Git - rust.git/blob - src/librustuv/file.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[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: 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 }