]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/fs2.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[rust.git] / src / libstd / sys / unix / fs2.rs
1 // Copyright 2013-2014 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 core::prelude::*;
12 use io::prelude::*;
13 use os::unix::prelude::*;
14
15 use ffi::{CString, CStr, OsString, AsOsStr, OsStr};
16 use io::{self, Error, Seek, SeekFrom};
17 use libc::{self, c_int, c_void, size_t, off_t, c_char, mode_t};
18 use mem;
19 use path::{Path, PathBuf};
20 use ptr;
21 use rc::Rc;
22 use sys::fd::FileDesc;
23 use sys::{c, cvt, cvt_r};
24 use sys_common::FromInner;
25 use vec::Vec;
26
27 pub struct File(FileDesc);
28
29 pub struct FileAttr {
30     stat: libc::stat,
31 }
32
33 pub struct ReadDir {
34     dirp: *mut libc::DIR,
35     root: Rc<PathBuf>,
36 }
37
38 pub struct DirEntry {
39     buf: Vec<u8>,
40     dirent: *mut libc::dirent_t,
41     root: Rc<PathBuf>,
42 }
43
44 #[derive(Clone)]
45 pub struct OpenOptions {
46     flags: c_int,
47     read: bool,
48     write: bool,
49     mode: mode_t,
50 }
51
52 #[derive(Clone, PartialEq, Eq, Debug)]
53 pub struct FilePermissions { mode: mode_t }
54
55 impl FileAttr {
56     pub fn is_dir(&self) -> bool {
57         (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFDIR
58     }
59     pub fn is_file(&self) -> bool {
60         (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFREG
61     }
62     pub fn size(&self) -> u64 { self.stat.st_size as u64 }
63     pub fn perm(&self) -> FilePermissions {
64         FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 }
65     }
66
67     pub fn accessed(&self) -> u64 {
68         self.mktime(self.stat.st_atime as u64, self.stat.st_atime_nsec as u64)
69     }
70     pub fn modified(&self) -> u64 {
71         self.mktime(self.stat.st_mtime as u64, self.stat.st_mtime_nsec as u64)
72     }
73
74     // times are in milliseconds (currently)
75     fn mktime(&self, secs: u64, nsecs: u64) -> u64 {
76         secs * 1000 + nsecs / 1000000
77     }
78 }
79
80 impl FilePermissions {
81     pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 }
82     pub fn set_readonly(&mut self, readonly: bool) {
83         if readonly {
84             self.mode &= !0o222;
85         } else {
86             self.mode |= 0o222;
87         }
88     }
89 }
90
91 impl FromInner<i32> for FilePermissions {
92     fn from_inner(mode: i32) -> FilePermissions {
93         FilePermissions { mode: mode as mode_t }
94     }
95 }
96
97 impl Iterator for ReadDir {
98     type Item = io::Result<DirEntry>;
99
100     fn next(&mut self) -> Option<io::Result<DirEntry>> {
101         extern {
102             fn rust_dirent_t_size() -> c_int;
103         }
104
105         let mut buf: Vec<u8> = Vec::with_capacity(unsafe {
106             rust_dirent_t_size() as usize
107         });
108         let ptr = buf.as_mut_ptr() as *mut libc::dirent_t;
109
110         let mut entry_ptr = ptr::null_mut();
111         loop {
112             if unsafe { libc::readdir_r(self.dirp, ptr, &mut entry_ptr) != 0 } {
113                 return Some(Err(Error::last_os_error()))
114             }
115             if entry_ptr.is_null() {
116                 return None
117             }
118
119             let entry = DirEntry {
120                 buf: buf,
121                 dirent: entry_ptr,
122                 root: self.root.clone()
123             };
124             if entry.name_bytes() == b"." || entry.name_bytes() == b".." {
125                 buf = entry.buf;
126             } else {
127                 return Some(Ok(entry))
128             }
129         }
130     }
131 }
132
133 impl Drop for ReadDir {
134     fn drop(&mut self) {
135         let r = unsafe { libc::closedir(self.dirp) };
136         debug_assert_eq!(r, 0);
137     }
138 }
139
140 impl DirEntry {
141     pub fn path(&self) -> PathBuf {
142         self.root.join(<OsStr as OsStrExt>::from_bytes(self.name_bytes()))
143     }
144
145     fn name_bytes(&self) -> &[u8] {
146         extern {
147             fn rust_list_dir_val(ptr: *mut libc::dirent_t) -> *const c_char;
148         }
149         unsafe {
150             CStr::from_ptr(rust_list_dir_val(self.dirent)).to_bytes()
151         }
152     }
153 }
154
155 impl OpenOptions {
156     pub fn new() -> OpenOptions {
157         OpenOptions {
158             flags: 0,
159             read: false,
160             write: false,
161             mode: 0o666,
162         }
163     }
164
165     pub fn read(&mut self, read: bool) {
166         self.read = read;
167     }
168
169     pub fn write(&mut self, write: bool) {
170         self.write = write;
171     }
172
173     pub fn append(&mut self, append: bool) {
174         self.flag(libc::O_APPEND, append);
175     }
176
177     pub fn truncate(&mut self, truncate: bool) {
178         self.flag(libc::O_TRUNC, truncate);
179     }
180
181     pub fn create(&mut self, create: bool) {
182         self.flag(libc::O_CREAT, create);
183     }
184
185     pub fn mode(&mut self, mode: i32) {
186         self.mode = mode as mode_t;
187     }
188
189     fn flag(&mut self, bit: c_int, on: bool) {
190         if on {
191             self.flags |= bit;
192         } else {
193             self.flags &= !bit;
194         }
195     }
196 }
197
198 impl File {
199     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
200         let flags = opts.flags | match (opts.read, opts.write) {
201             (true, true) => libc::O_RDWR,
202             (false, true) => libc::O_WRONLY,
203             (true, false) |
204             (false, false) => libc::O_RDONLY,
205         };
206         let path = try!(cstr(path));
207         let fd = try!(cvt_r(|| unsafe {
208             libc::open(path.as_ptr(), flags, opts.mode)
209         }));
210         Ok(File(FileDesc::new(fd)))
211     }
212
213     pub fn file_attr(&self) -> io::Result<FileAttr> {
214         let mut stat: libc::stat = unsafe { mem::zeroed() };
215         try!(cvt(unsafe { libc::fstat(self.0.raw(), &mut stat) }));
216         Ok(FileAttr { stat: stat })
217     }
218
219     pub fn fsync(&self) -> io::Result<()> {
220         try!(cvt_r(|| unsafe { libc::fsync(self.0.raw()) }));
221         Ok(())
222     }
223
224     pub fn datasync(&self) -> io::Result<()> {
225         try!(cvt_r(|| unsafe { os_datasync(self.0.raw()) }));
226         return Ok(());
227
228         #[cfg(any(target_os = "macos", target_os = "ios"))]
229         unsafe fn os_datasync(fd: c_int) -> c_int {
230             libc::fcntl(fd, libc::F_FULLFSYNC)
231         }
232         #[cfg(target_os = "linux")]
233         unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) }
234         #[cfg(not(any(target_os = "macos",
235                       target_os = "ios",
236                       target_os = "linux")))]
237         unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
238     }
239
240     pub fn truncate(&self, size: u64) -> io::Result<()> {
241         try!(cvt_r(|| unsafe {
242             libc::ftruncate(self.0.raw(), size as libc::off_t)
243         }));
244         Ok(())
245     }
246
247     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
248         self.0.read(buf)
249     }
250
251     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
252         self.0.write(buf)
253     }
254
255     pub fn flush(&self) -> io::Result<()> { Ok(()) }
256
257     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
258         let (whence, pos) = match pos {
259             SeekFrom::Start(off) => (libc::SEEK_SET, off as off_t),
260             SeekFrom::End(off) => (libc::SEEK_END, off as off_t),
261             SeekFrom::Current(off) => (libc::SEEK_CUR, off as off_t),
262         };
263         let n = try!(cvt(unsafe { libc::lseek(self.0.raw(), pos, whence) }));
264         Ok(n as u64)
265     }
266
267     pub fn fd(&self) -> &FileDesc { &self.0 }
268 }
269
270 fn cstr(path: &Path) -> io::Result<CString> {
271     let cstring = try!(path.as_os_str().to_cstring());
272     Ok(cstring)
273 }
274
275 pub fn mkdir(p: &Path) -> io::Result<()> {
276     let p = try!(cstr(p));
277     try!(cvt(unsafe { libc::mkdir(p.as_ptr(), 0o777) }));
278     Ok(())
279 }
280
281 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
282     let root = Rc::new(p.to_path_buf());
283     let p = try!(cstr(p));
284     unsafe {
285         let ptr = libc::opendir(p.as_ptr());
286         if ptr.is_null() {
287             Err(Error::last_os_error())
288         } else {
289             Ok(ReadDir { dirp: ptr, root: root })
290         }
291     }
292 }
293
294 pub fn unlink(p: &Path) -> io::Result<()> {
295     let p = try!(cstr(p));
296     try!(cvt(unsafe { libc::unlink(p.as_ptr()) }));
297     Ok(())
298 }
299
300 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
301     let old = try!(cstr(old));
302     let new = try!(cstr(new));
303     try!(cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }));
304     Ok(())
305 }
306
307 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
308     let p = try!(cstr(p));
309     try!(cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }));
310     Ok(())
311 }
312
313 pub fn rmdir(p: &Path) -> io::Result<()> {
314     let p = try!(cstr(p));
315     try!(cvt(unsafe { libc::rmdir(p.as_ptr()) }));
316     Ok(())
317 }
318
319 pub fn chown(p: &Path, uid: isize, gid: isize) -> io::Result<()> {
320     let p = try!(cstr(p));
321     try!(cvt_r(|| unsafe {
322         libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
323     }));
324     Ok(())
325 }
326
327 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
328     let c_path = try!(cstr(p));
329     let p = c_path.as_ptr();
330     let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
331     if len < 0 {
332         len = 1024; // FIXME: read PATH_MAX from C ffi?
333     }
334     let mut buf: Vec<u8> = Vec::with_capacity(len as usize);
335     unsafe {
336         let n = try!(cvt({
337             libc::readlink(p, buf.as_ptr() as *mut c_char, len as size_t)
338         }));
339         buf.set_len(n as usize);
340     }
341     let s: OsString = OsStringExt::from_vec(buf);
342     Ok(PathBuf::new(&s))
343 }
344
345 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
346     let src = try!(cstr(src));
347     let dst = try!(cstr(dst));
348     try!(cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }));
349     Ok(())
350 }
351
352 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
353     let src = try!(cstr(src));
354     let dst = try!(cstr(dst));
355     try!(cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }));
356     Ok(())
357 }
358
359 pub fn stat(p: &Path) -> io::Result<FileAttr> {
360     let p = try!(cstr(p));
361     let mut stat: libc::stat = unsafe { mem::zeroed() };
362     try!(cvt(unsafe { libc::stat(p.as_ptr(), &mut stat) }));
363     Ok(FileAttr { stat: stat })
364 }
365
366 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
367     let p = try!(cstr(p));
368     let mut stat: libc::stat = unsafe { mem::zeroed() };
369     try!(cvt(unsafe { libc::lstat(p.as_ptr(), &mut stat) }));
370     Ok(FileAttr { stat: stat })
371 }
372
373 pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
374     let p = try!(cstr(p));
375     let buf = [super::ms_to_timeval(atime), super::ms_to_timeval(mtime)];
376     try!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) }));
377     Ok(())
378 }