]> git.lizzy.rs Git - rust.git/blob - src/librustuv/file.rs
auto merge of #15421 : catharsis/rust/doc-ffi-minor-fixes, 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 libc::{c_int, c_char, c_void, ssize_t};
12 use libc;
13 use std::c_str::CString;
14 use std::c_str;
15 use std::mem;
16 use std::os;
17 use std::rt::rtio::{IoResult, IoError};
18 use std::rt::rtio;
19 use std::rt::task::BlockedTask;
20
21 use homing::{HomingIO, HomeHandle};
22 use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup};
23 use uvio::UvIoFactory;
24 use uvll;
25
26 pub struct FsRequest {
27     req: *mut uvll::uv_fs_t,
28     fired: bool,
29 }
30
31 pub struct FileWatcher {
32     loop_: Loop,
33     fd: c_int,
34     close: rtio::CloseBehavior,
35     home: HomeHandle,
36 }
37
38 impl FsRequest {
39     pub fn open(io: &mut UvIoFactory, path: &CString, flags: int, mode: int)
40         -> Result<FileWatcher, UvError>
41     {
42         execute(|req, cb| unsafe {
43             uvll::uv_fs_open(io.uv_loop(),
44                              req, path.as_ptr(), flags as c_int,
45                              mode as c_int, cb)
46         }).map(|req|
47             FileWatcher::new(io, req.get_result() as c_int,
48                              rtio::CloseSynchronously)
49         )
50     }
51
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(),
55                                cb)
56         })
57     }
58
59     pub fn lstat(loop_: &Loop, path: &CString)
60         -> Result<rtio::FileStat, UvError>
61     {
62         execute(|req, cb| unsafe {
63             uvll::uv_fs_lstat(loop_.handle, req, path.as_ptr(),
64                               cb)
65         }).map(|req| req.mkstat())
66     }
67
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(),
71                              cb)
72         }).map(|req| req.mkstat())
73     }
74
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())
79     }
80
81     pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
82         -> Result<(), UvError>
83     {
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.
89         let mut written = 0;
90         while written < buf.len() {
91             let offset = if offset == -1 {
92                 offset
93             } else {
94                 offset + written as i64
95             };
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,
99             };
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; }
105             }
106         }
107         Ok(())
108     }
109
110     pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
111         -> Result<int, UvError>
112     {
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,
117             };
118             uvll::uv_fs_read(loop_.handle, req, fd, &mut uvbuf, 1, offset, cb)
119         }).map(|req| {
120             req.get_result() as int
121         })
122     }
123
124     pub fn mkdir(loop_: &Loop, path: &CString, mode: c_int)
125         -> Result<(), UvError>
126     {
127         execute_nop(|req, cb| unsafe {
128             uvll::uv_fs_mkdir(loop_.handle, req, path.as_ptr(),
129                               mode, cb)
130         })
131     }
132
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(),
136                               cb)
137         })
138     }
139
140     pub fn rename(loop_: &Loop, path: &CString, to: &CString)
141         -> Result<(), UvError>
142     {
143         execute_nop(|req, cb| unsafe {
144             uvll::uv_fs_rename(loop_.handle,
145                                req,
146                                path.as_ptr(),
147                                to.as_ptr(),
148                                cb)
149         })
150     }
151
152     pub fn chmod(loop_: &Loop, path: &CString, mode: c_int)
153         -> Result<(), UvError>
154     {
155         execute_nop(|req, cb| unsafe {
156             uvll::uv_fs_chmod(loop_.handle, req, path.as_ptr(),
157                               mode, cb)
158         })
159     }
160
161     pub fn readdir(loop_: &Loop, path: &CString, flags: c_int)
162         -> Result<Vec<CString>, UvError>
163     {
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),
173                                               |rel| {
174                 let p = rel.as_bytes();
175                 paths.push(parent.join(p.slice_to(rel.len())).to_c_str());
176             });
177             paths
178         })
179     }
180
181     pub fn readlink(loop_: &Loop, path: &CString) -> Result<CString, UvError> {
182         execute(|req, cb| unsafe {
183             uvll::uv_fs_readlink(loop_.handle, req,
184                                  path.as_ptr(), cb)
185         }).map(|req| {
186             // Be sure to clone the cstring so we get an independently owned
187             // allocation to work with and return.
188             unsafe {
189                 CString::new(req.get_ptr() as *const libc::c_char, false).clone()
190             }
191         })
192     }
193
194     pub fn chown(loop_: &Loop, path: &CString, uid: int, gid: int)
195         -> Result<(), UvError>
196     {
197         execute_nop(|req, cb| unsafe {
198             uvll::uv_fs_chown(loop_.handle,
199                               req, path.as_ptr(),
200                               uid as uvll::uv_uid_t,
201                               gid as uvll::uv_gid_t,
202                               cb)
203         })
204     }
205
206     pub fn truncate(loop_: &Loop, file: c_int, offset: i64)
207         -> Result<(), UvError>
208     {
209         execute_nop(|req, cb| unsafe {
210             uvll::uv_fs_ftruncate(loop_.handle, req, file, offset, cb)
211         })
212     }
213
214     pub fn link(loop_: &Loop, src: &CString, dst: &CString)
215         -> Result<(), UvError>
216     {
217         execute_nop(|req, cb| unsafe {
218             uvll::uv_fs_link(loop_.handle, req,
219                              src.as_ptr(),
220                              dst.as_ptr(),
221                              cb)
222         })
223     }
224
225     pub fn symlink(loop_: &Loop, src: &CString, dst: &CString)
226         -> Result<(), UvError>
227     {
228         execute_nop(|req, cb| unsafe {
229             uvll::uv_fs_symlink(loop_.handle, req,
230                                 src.as_ptr(),
231                                 dst.as_ptr(),
232                                 0, cb)
233         })
234     }
235
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)
239         })
240     }
241
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)
245         })
246     }
247
248     pub fn utime(loop_: &Loop, path: &CString, atime: u64, mtime: u64)
249         -> Result<(), UvError>
250     {
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(),
256                               atime, mtime, cb)
257         })
258     }
259
260     pub fn get_result(&self) -> ssize_t {
261         unsafe { uvll::get_result_from_fs_req(self.req) }
262     }
263
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); }
267         stat
268     }
269
270     pub fn get_ptr(&self) -> *mut libc::c_void {
271         unsafe { uvll::get_ptr_from_fs_req(self.req) }
272     }
273
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
280         }
281         rtio::FileStat {
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,
298         }
299     }
300 }
301
302 impl Drop for FsRequest {
303     fn drop(&mut self) {
304         unsafe {
305             if self.fired {
306                 uvll::uv_fs_req_cleanup(self.req);
307             }
308             uvll::free_req(self.req);
309         }
310     }
311 }
312
313 fn execute(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
314     -> Result<FsRequest, UvError>
315 {
316     let mut req = FsRequest {
317         fired: false,
318         req: unsafe { uvll::malloc_req(uvll::UV_FS) }
319     };
320     return match f(req.req, fs_cb) {
321         0 => {
322             req.fired = true;
323             let mut slot = None;
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) }
327             });
328             match req.get_result() {
329                 n if n < 0 => Err(UvError(n as i32)),
330                 _ => Ok(req),
331             }
332         }
333         n => Err(UvError(n))
334     };
335
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))
339         };
340         wakeup(slot);
341     }
342 }
343
344 fn execute_nop(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
345     -> Result<(), UvError> {
346     execute(f).map(|_| {})
347 }
348
349 impl HomingIO for FileWatcher {
350     fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
351 }
352
353 impl FileWatcher {
354     pub fn new(io: &mut UvIoFactory, fd: c_int,
355                close: rtio::CloseBehavior) -> FileWatcher {
356         FileWatcher {
357             loop_: Loop::wrap(io.uv_loop()),
358             fd: fd,
359             close: close,
360             home: io.make_handle(),
361         }
362     }
363
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)
368     }
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)
373     }
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) } {
376             -1 => {
377                 Err(IoError {
378                     code: os::errno() as uint,
379                     extra: 0,
380                     detail: None,
381                 })
382             },
383             n => Ok(n as u64)
384         }
385     }
386 }
387
388 impl Drop for FileWatcher {
389     fn drop(&mut self) {
390         let _m = self.fire_homing_missile();
391         match self.close {
392             rtio::DontClose => {}
393             rtio::CloseAsynchronously => {
394                 unsafe {
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);
398                 }
399
400                 extern fn close_cb(req: *mut uvll::uv_fs_t) {
401                     unsafe {
402                         uvll::uv_fs_req_cleanup(req);
403                         uvll::free_req(req);
404                     }
405                 }
406             }
407             rtio::CloseSynchronously => {
408                 let _ = execute_nop(|req, cb| unsafe {
409                     uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
410                 });
411             }
412         }
413     }
414 }
415
416 impl rtio::RtioFileStream for FileWatcher {
417     fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
418         self.base_read(buf, -1)
419     }
420     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
421         self.base_write(buf, -1)
422     }
423     fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
424         self.base_read(buf, offset as i64)
425     }
426     fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
427         self.base_write(buf, offset as i64)
428     }
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
435         };
436         self.seek_common(pos, whence)
437     }
438     fn tell(&self) -> IoResult<u64> {
439         use libc::SEEK_CUR;
440
441         self.seek_common(0, SEEK_CUR)
442     }
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)
446     }
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)
450     }
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)
455     }
456
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)
460     }
461 }
462
463 #[cfg(test)]
464 mod test {
465     use libc::c_int;
466     use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
467     use std::str;
468     use super::FsRequest;
469     use super::super::Loop;
470     use super::super::local_loop;
471
472     fn l() -> &mut Loop { &mut local_loop().loop_ }
473
474     #[test]
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";
480
481         {
482             // open/create
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();
487             let fd = result.fd;
488
489             // write
490             let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
491             assert!(result.is_ok());
492         }
493
494         {
495             // re-open
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();
500             let fd = result.fd;
501
502             // read
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());
506
507             let nread = result.unwrap();
508             assert!(nread > 0);
509             let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
510             assert_eq!(read_str, "hello");
511         }
512         // unlink
513         let result = FsRequest::unlink(l(), &path_str.to_c_str());
514         assert!(result.is_ok());
515     }
516
517     #[test]
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;
522
523         let result = FsRequest::open(local_loop(), path, create_flags, mode);
524         assert!(result.is_ok());
525         let file = result.unwrap();
526
527         let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
528         assert!(result.is_ok());
529
530         let result = FsRequest::stat(l(), path);
531         assert!(result.is_ok());
532         assert_eq!(result.unwrap().size, 5);
533
534         let result = FsRequest::fstat(l(), file.fd);
535         assert!(result.is_ok());
536         assert_eq!(result.unwrap().size, 5);
537
538         fn free<T>(_: T) {}
539         free(file);
540
541         let result = FsRequest::unlink(l(), path);
542         assert!(result.is_ok());
543     }
544
545     #[test]
546     fn file_test_mk_rm_dir() {
547         let path = &"./tmp/mk_rm_dir".to_c_str();
548         let mode = S_IWUSR | S_IRUSR;
549
550         let result = FsRequest::mkdir(l(), path, mode as c_int);
551         assert!(result.is_ok());
552
553         let result = FsRequest::rmdir(l(), path);
554         assert!(result.is_ok());
555
556         let result = FsRequest::stat(l(), path);
557         assert!(result.is_err());
558     }
559
560     #[test]
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;
564
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);
573     }
574
575     #[test]
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());
580     }
581 }