]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
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 | ||
7453a54e | 11 | use prelude::v1::*; |
85aaf69f SL |
12 | use io::prelude::*; |
13 | use os::unix::prelude::*; | |
14 | ||
c34b1796 | 15 | use ffi::{CString, CStr, OsString, OsStr}; |
d9579d0f | 16 | use fmt; |
c1a9b12d | 17 | use io::{self, Error, ErrorKind, SeekFrom}; |
7453a54e | 18 | use libc::{self, c_int, mode_t}; |
85aaf69f SL |
19 | use mem; |
20 | use path::{Path, PathBuf}; | |
21 | use ptr; | |
c34b1796 | 22 | use sync::Arc; |
85aaf69f | 23 | use sys::fd::FileDesc; |
7453a54e | 24 | use sys::time::SystemTime; |
92a42be0 | 25 | use sys::{cvt, cvt_r}; |
d9579d0f | 26 | use sys_common::{AsInner, FromInner}; |
7453a54e | 27 | |
54a0048b | 28 | #[cfg(any(target_os = "linux", target_os = "emscripten"))] |
7453a54e SL |
29 | use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64}; |
30 | #[cfg(target_os = "android")] | |
31 | use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off64_t, ftruncate64, lseek64, | |
32 | dirent as dirent64, open as open64}; | |
54a0048b SL |
33 | #[cfg(not(any(target_os = "linux", |
34 | target_os = "emscripten", | |
35 | target_os = "android")))] | |
7453a54e SL |
36 | use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t, |
37 | ftruncate as ftruncate64, lseek as lseek64, dirent as dirent64, open as open64}; | |
54a0048b SL |
38 | #[cfg(not(any(target_os = "linux", |
39 | target_os = "emscripten", | |
40 | target_os = "solaris")))] | |
7453a54e | 41 | use libc::{readdir_r as readdir64_r}; |
85aaf69f SL |
42 | |
43 | pub struct File(FileDesc); | |
44 | ||
b039eaaf | 45 | #[derive(Clone)] |
85aaf69f | 46 | pub struct FileAttr { |
7453a54e | 47 | stat: stat64, |
85aaf69f SL |
48 | } |
49 | ||
50 | pub struct ReadDir { | |
c34b1796 AL |
51 | dirp: Dir, |
52 | root: Arc<PathBuf>, | |
85aaf69f SL |
53 | } |
54 | ||
c34b1796 AL |
55 | struct Dir(*mut libc::DIR); |
56 | ||
57 | unsafe impl Send for Dir {} | |
58 | unsafe impl Sync for Dir {} | |
59 | ||
85aaf69f | 60 | pub struct DirEntry { |
7453a54e | 61 | entry: dirent64, |
c34b1796 | 62 | root: Arc<PathBuf>, |
7453a54e SL |
63 | // We need to store an owned copy of the directory name |
64 | // on Solaris because a) it uses a zero-length array to | |
65 | // store the name, b) its lifetime between readdir calls | |
66 | // is not guaranteed. | |
67 | #[cfg(target_os = "solaris")] | |
68 | name: Box<[u8]> | |
85aaf69f SL |
69 | } |
70 | ||
71 | #[derive(Clone)] | |
72 | pub struct OpenOptions { | |
7453a54e | 73 | // generic |
85aaf69f SL |
74 | read: bool, |
75 | write: bool, | |
7453a54e SL |
76 | append: bool, |
77 | truncate: bool, | |
78 | create: bool, | |
79 | create_new: bool, | |
80 | // system-specific | |
81 | custom_flags: i32, | |
85aaf69f SL |
82 | mode: mode_t, |
83 | } | |
84 | ||
85 | #[derive(Clone, PartialEq, Eq, Debug)] | |
86 | pub struct FilePermissions { mode: mode_t } | |
87 | ||
d9579d0f AL |
88 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
89 | pub struct FileType { mode: mode_t } | |
90 | ||
91 | pub struct DirBuilder { mode: mode_t } | |
92 | ||
85aaf69f | 93 | impl FileAttr { |
85aaf69f SL |
94 | pub fn size(&self) -> u64 { self.stat.st_size as u64 } |
95 | pub fn perm(&self) -> FilePermissions { | |
96 | FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } | |
97 | } | |
98 | ||
d9579d0f AL |
99 | pub fn file_type(&self) -> FileType { |
100 | FileType { mode: self.stat.st_mode as mode_t } | |
85aaf69f | 101 | } |
d9579d0f | 102 | } |
85aaf69f | 103 | |
7453a54e SL |
104 | #[cfg(any(target_os = "ios", target_os = "macos"))] |
105 | // FIXME: update SystemTime to store a timespec and don't lose precision | |
106 | impl FileAttr { | |
107 | pub fn modified(&self) -> io::Result<SystemTime> { | |
108 | Ok(SystemTime::from(libc::timeval { | |
109 | tv_sec: self.stat.st_mtime, | |
110 | tv_usec: (self.stat.st_mtime_nsec / 1000) as libc::suseconds_t, | |
111 | })) | |
112 | } | |
113 | ||
114 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
115 | Ok(SystemTime::from(libc::timeval { | |
116 | tv_sec: self.stat.st_atime, | |
117 | tv_usec: (self.stat.st_atime_nsec / 1000) as libc::suseconds_t, | |
118 | })) | |
119 | } | |
120 | ||
121 | pub fn created(&self) -> io::Result<SystemTime> { | |
122 | Ok(SystemTime::from(libc::timeval { | |
123 | tv_sec: self.stat.st_birthtime, | |
124 | tv_usec: (self.stat.st_birthtime_nsec / 1000) as libc::suseconds_t, | |
125 | })) | |
126 | } | |
d9579d0f AL |
127 | } |
128 | ||
7453a54e SL |
129 | #[cfg(target_os = "netbsd")] |
130 | impl FileAttr { | |
131 | pub fn modified(&self) -> io::Result<SystemTime> { | |
132 | Ok(SystemTime::from(libc::timespec { | |
133 | tv_sec: self.stat.st_mtime as libc::time_t, | |
134 | tv_nsec: self.stat.st_mtimensec as libc::c_long, | |
135 | })) | |
136 | } | |
137 | ||
138 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
139 | Ok(SystemTime::from(libc::timespec { | |
140 | tv_sec: self.stat.st_atime as libc::time_t, | |
141 | tv_nsec: self.stat.st_atimensec as libc::c_long, | |
142 | })) | |
143 | } | |
144 | ||
145 | pub fn created(&self) -> io::Result<SystemTime> { | |
146 | Ok(SystemTime::from(libc::timespec { | |
147 | tv_sec: self.stat.st_birthtime as libc::time_t, | |
148 | tv_nsec: self.stat.st_birthtimensec as libc::c_long, | |
149 | })) | |
150 | } | |
d9579d0f AL |
151 | } |
152 | ||
7453a54e SL |
153 | #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))] |
154 | impl FileAttr { | |
155 | pub fn modified(&self) -> io::Result<SystemTime> { | |
156 | Ok(SystemTime::from(libc::timespec { | |
157 | tv_sec: self.stat.st_mtime as libc::time_t, | |
158 | tv_nsec: self.stat.st_mtime_nsec as libc::c_long, | |
159 | })) | |
160 | } | |
161 | ||
162 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
163 | Ok(SystemTime::from(libc::timespec { | |
164 | tv_sec: self.stat.st_atime as libc::time_t, | |
165 | tv_nsec: self.stat.st_atime_nsec as libc::c_long, | |
166 | })) | |
167 | } | |
168 | ||
169 | #[cfg(any(target_os = "bitrig", | |
170 | target_os = "freebsd", | |
171 | target_os = "openbsd"))] | |
172 | pub fn created(&self) -> io::Result<SystemTime> { | |
173 | Ok(SystemTime::from(libc::timespec { | |
174 | tv_sec: self.stat.st_birthtime as libc::time_t, | |
175 | tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, | |
176 | })) | |
177 | } | |
178 | ||
179 | #[cfg(not(any(target_os = "bitrig", | |
180 | target_os = "freebsd", | |
181 | target_os = "openbsd")))] | |
182 | pub fn created(&self) -> io::Result<SystemTime> { | |
183 | Err(io::Error::new(io::ErrorKind::Other, | |
184 | "creation time is not available on this platform \ | |
185 | currently")) | |
186 | } | |
187 | } | |
188 | ||
189 | impl AsInner<stat64> for FileAttr { | |
190 | fn as_inner(&self) -> &stat64 { &self.stat } | |
85aaf69f SL |
191 | } |
192 | ||
193 | impl FilePermissions { | |
194 | pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } | |
195 | pub fn set_readonly(&mut self, readonly: bool) { | |
196 | if readonly { | |
197 | self.mode &= !0o222; | |
198 | } else { | |
199 | self.mode |= 0o222; | |
200 | } | |
201 | } | |
7453a54e | 202 | pub fn mode(&self) -> u32 { self.mode as u32 } |
d9579d0f AL |
203 | } |
204 | ||
205 | impl FileType { | |
206 | pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } | |
207 | pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } | |
208 | pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } | |
209 | ||
c1a9b12d | 210 | pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } |
85aaf69f SL |
211 | } |
212 | ||
7453a54e SL |
213 | impl FromInner<u32> for FilePermissions { |
214 | fn from_inner(mode: u32) -> FilePermissions { | |
85aaf69f SL |
215 | FilePermissions { mode: mode as mode_t } |
216 | } | |
217 | } | |
218 | ||
219 | impl Iterator for ReadDir { | |
220 | type Item = io::Result<DirEntry>; | |
221 | ||
7453a54e SL |
222 | #[cfg(target_os = "solaris")] |
223 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
224 | unsafe { | |
225 | loop { | |
226 | // Although readdir_r(3) would be a correct function to use here because | |
227 | // of the thread safety, on Illumos the readdir(3C) function is safe to use | |
228 | // in threaded applications and it is generally preferred over the | |
229 | // readdir_r(3C) function. | |
230 | let entry_ptr = libc::readdir(self.dirp.0); | |
231 | if entry_ptr.is_null() { | |
232 | return None | |
233 | } | |
234 | ||
235 | let name = (*entry_ptr).d_name.as_ptr(); | |
236 | let namelen = libc::strlen(name) as usize; | |
237 | ||
238 | let ret = DirEntry { | |
239 | entry: *entry_ptr, | |
240 | name: ::slice::from_raw_parts(name as *const u8, | |
241 | namelen as usize).to_owned().into_boxed_slice(), | |
242 | root: self.root.clone() | |
243 | }; | |
244 | if ret.name_bytes() != b"." && ret.name_bytes() != b".." { | |
245 | return Some(Ok(ret)) | |
246 | } | |
247 | } | |
248 | } | |
249 | } | |
250 | ||
251 | #[cfg(not(target_os = "solaris"))] | |
85aaf69f | 252 | fn next(&mut self) -> Option<io::Result<DirEntry>> { |
9cc50fc6 SL |
253 | unsafe { |
254 | let mut ret = DirEntry { | |
255 | entry: mem::zeroed(), | |
85aaf69f SL |
256 | root: self.root.clone() |
257 | }; | |
9cc50fc6 SL |
258 | let mut entry_ptr = ptr::null_mut(); |
259 | loop { | |
7453a54e | 260 | if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { |
9cc50fc6 SL |
261 | return Some(Err(Error::last_os_error())) |
262 | } | |
263 | if entry_ptr.is_null() { | |
264 | return None | |
265 | } | |
266 | if ret.name_bytes() != b"." && ret.name_bytes() != b".." { | |
267 | return Some(Ok(ret)) | |
268 | } | |
85aaf69f SL |
269 | } |
270 | } | |
271 | } | |
272 | } | |
273 | ||
c34b1796 | 274 | impl Drop for Dir { |
85aaf69f | 275 | fn drop(&mut self) { |
c34b1796 | 276 | let r = unsafe { libc::closedir(self.0) }; |
85aaf69f SL |
277 | debug_assert_eq!(r, 0); |
278 | } | |
279 | } | |
280 | ||
281 | impl DirEntry { | |
282 | pub fn path(&self) -> PathBuf { | |
9cc50fc6 | 283 | self.root.join(OsStr::from_bytes(self.name_bytes())) |
85aaf69f SL |
284 | } |
285 | ||
d9579d0f AL |
286 | pub fn file_name(&self) -> OsString { |
287 | OsStr::from_bytes(self.name_bytes()).to_os_string() | |
288 | } | |
289 | ||
290 | pub fn metadata(&self) -> io::Result<FileAttr> { | |
291 | lstat(&self.path()) | |
292 | } | |
293 | ||
7453a54e SL |
294 | #[cfg(target_os = "solaris")] |
295 | pub fn file_type(&self) -> io::Result<FileType> { | |
296 | stat(&self.path()).map(|m| m.file_type()) | |
297 | } | |
298 | ||
299 | #[cfg(not(target_os = "solaris"))] | |
d9579d0f | 300 | pub fn file_type(&self) -> io::Result<FileType> { |
9cc50fc6 SL |
301 | match self.entry.d_type { |
302 | libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), | |
303 | libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), | |
304 | libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), | |
305 | libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), | |
306 | libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), | |
307 | libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), | |
308 | libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), | |
309 | _ => lstat(&self.path()).map(|m| m.file_type()), | |
d9579d0f AL |
310 | } |
311 | } | |
312 | ||
9cc50fc6 SL |
313 | #[cfg(any(target_os = "macos", |
314 | target_os = "ios", | |
7453a54e SL |
315 | target_os = "linux", |
316 | target_os = "emscripten", | |
317 | target_os = "android", | |
318 | target_os = "solaris"))] | |
319 | pub fn ino(&self) -> u64 { | |
320 | self.entry.d_ino as u64 | |
9cc50fc6 SL |
321 | } |
322 | ||
323 | #[cfg(any(target_os = "freebsd", | |
324 | target_os = "openbsd", | |
325 | target_os = "bitrig", | |
326 | target_os = "netbsd", | |
327 | target_os = "dragonfly"))] | |
7453a54e SL |
328 | pub fn ino(&self) -> u64 { |
329 | self.entry.d_fileno as u64 | |
d9579d0f AL |
330 | } |
331 | ||
9cc50fc6 SL |
332 | #[cfg(any(target_os = "macos", |
333 | target_os = "ios", | |
334 | target_os = "netbsd", | |
7453a54e SL |
335 | target_os = "openbsd", |
336 | target_os = "freebsd", | |
9cc50fc6 SL |
337 | target_os = "dragonfly", |
338 | target_os = "bitrig"))] | |
339 | fn name_bytes(&self) -> &[u8] { | |
85aaf69f | 340 | unsafe { |
9cc50fc6 | 341 | ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8, |
7453a54e | 342 | self.entry.d_namlen as usize) |
85aaf69f SL |
343 | } |
344 | } | |
9cc50fc6 | 345 | #[cfg(any(target_os = "android", |
7453a54e SL |
346 | target_os = "linux", |
347 | target_os = "emscripten"))] | |
9cc50fc6 SL |
348 | fn name_bytes(&self) -> &[u8] { |
349 | unsafe { | |
350 | CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() | |
351 | } | |
c34b1796 | 352 | } |
7453a54e SL |
353 | #[cfg(target_os = "solaris")] |
354 | fn name_bytes(&self) -> &[u8] { | |
355 | &*self.name | |
356 | } | |
85aaf69f SL |
357 | } |
358 | ||
359 | impl OpenOptions { | |
360 | pub fn new() -> OpenOptions { | |
361 | OpenOptions { | |
7453a54e | 362 | // generic |
85aaf69f SL |
363 | read: false, |
364 | write: false, | |
7453a54e SL |
365 | append: false, |
366 | truncate: false, | |
367 | create: false, | |
368 | create_new: false, | |
369 | // system-specific | |
370 | custom_flags: 0, | |
85aaf69f SL |
371 | mode: 0o666, |
372 | } | |
373 | } | |
374 | ||
7453a54e SL |
375 | pub fn read(&mut self, read: bool) { self.read = read; } |
376 | pub fn write(&mut self, write: bool) { self.write = write; } | |
377 | pub fn append(&mut self, append: bool) { self.append = append; } | |
378 | pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } | |
379 | pub fn create(&mut self, create: bool) { self.create = create; } | |
380 | pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } | |
381 | ||
382 | pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } | |
383 | pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } | |
384 | ||
385 | fn get_access_mode(&self) -> io::Result<c_int> { | |
386 | match (self.read, self.write, self.append) { | |
387 | (true, false, false) => Ok(libc::O_RDONLY), | |
388 | (false, true, false) => Ok(libc::O_WRONLY), | |
389 | (true, true, false) => Ok(libc::O_RDWR), | |
390 | (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), | |
391 | (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), | |
392 | (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), | |
393 | } | |
85aaf69f SL |
394 | } |
395 | ||
7453a54e SL |
396 | fn get_creation_mode(&self) -> io::Result<c_int> { |
397 | match (self.write, self.append) { | |
398 | (true, false) => {} | |
399 | (false, false) => | |
400 | if self.truncate || self.create || self.create_new { | |
401 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
402 | }, | |
403 | (_, true) => | |
404 | if self.truncate && !self.create_new { | |
405 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
406 | }, | |
85aaf69f | 407 | } |
7453a54e SL |
408 | |
409 | Ok(match (self.create, self.truncate, self.create_new) { | |
410 | (false, false, false) => 0, | |
411 | (true, false, false) => libc::O_CREAT, | |
412 | (false, true, false) => libc::O_TRUNC, | |
413 | (true, true, false) => libc::O_CREAT | libc::O_TRUNC, | |
414 | (_, _, true) => libc::O_CREAT | libc::O_EXCL, | |
415 | }) | |
85aaf69f SL |
416 | } |
417 | } | |
418 | ||
419 | impl File { | |
420 | pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { | |
54a0048b | 421 | let path = cstr(path)?; |
9346a6ac AL |
422 | File::open_c(&path, opts) |
423 | } | |
424 | ||
425 | pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { | |
7453a54e | 426 | let flags = libc::O_CLOEXEC | |
54a0048b SL |
427 | opts.get_access_mode()? | |
428 | opts.get_creation_mode()? | | |
7453a54e | 429 | (opts.custom_flags as c_int & !libc::O_ACCMODE); |
54a0048b | 430 | let fd = cvt_r(|| unsafe { |
7453a54e | 431 | open64(path.as_ptr(), flags, opts.mode as c_int) |
54a0048b | 432 | })?; |
9346a6ac | 433 | let fd = FileDesc::new(fd); |
7453a54e SL |
434 | |
435 | // Currently the standard library supports Linux 2.6.18 which did not | |
436 | // have the O_CLOEXEC flag (passed above). If we're running on an older | |
437 | // Linux kernel then the flag is just ignored by the OS, so we continue | |
438 | // to explicitly ask for a CLOEXEC fd here. | |
439 | // | |
440 | // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc | |
441 | // that we support, so we only do this on Linux currently. | |
442 | if cfg!(target_os = "linux") { | |
443 | fd.set_cloexec(); | |
444 | } | |
445 | ||
9346a6ac | 446 | Ok(File(fd)) |
85aaf69f SL |
447 | } |
448 | ||
449 | pub fn file_attr(&self) -> io::Result<FileAttr> { | |
7453a54e | 450 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
54a0048b | 451 | cvt(unsafe { |
7453a54e | 452 | fstat64(self.0.raw(), &mut stat) |
54a0048b | 453 | })?; |
85aaf69f SL |
454 | Ok(FileAttr { stat: stat }) |
455 | } | |
456 | ||
457 | pub fn fsync(&self) -> io::Result<()> { | |
54a0048b | 458 | cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; |
85aaf69f SL |
459 | Ok(()) |
460 | } | |
461 | ||
462 | pub fn datasync(&self) -> io::Result<()> { | |
54a0048b | 463 | cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; |
85aaf69f SL |
464 | return Ok(()); |
465 | ||
466 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
467 | unsafe fn os_datasync(fd: c_int) -> c_int { | |
468 | libc::fcntl(fd, libc::F_FULLFSYNC) | |
469 | } | |
470 | #[cfg(target_os = "linux")] | |
471 | unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) } | |
472 | #[cfg(not(any(target_os = "macos", | |
473 | target_os = "ios", | |
474 | target_os = "linux")))] | |
475 | unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } | |
476 | } | |
477 | ||
478 | pub fn truncate(&self, size: u64) -> io::Result<()> { | |
54a0048b | 479 | cvt_r(|| unsafe { |
7453a54e | 480 | ftruncate64(self.0.raw(), size as off64_t) |
54a0048b | 481 | })?; |
85aaf69f SL |
482 | Ok(()) |
483 | } | |
484 | ||
485 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
486 | self.0.read(buf) | |
487 | } | |
488 | ||
54a0048b SL |
489 | pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { |
490 | self.0.read_to_end(buf) | |
491 | } | |
492 | ||
85aaf69f SL |
493 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
494 | self.0.write(buf) | |
495 | } | |
496 | ||
497 | pub fn flush(&self) -> io::Result<()> { Ok(()) } | |
498 | ||
499 | pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { | |
500 | let (whence, pos) = match pos { | |
7453a54e SL |
501 | SeekFrom::Start(off) => (libc::SEEK_SET, off as off64_t), |
502 | SeekFrom::End(off) => (libc::SEEK_END, off as off64_t), | |
503 | SeekFrom::Current(off) => (libc::SEEK_CUR, off as off64_t), | |
85aaf69f | 504 | }; |
54a0048b | 505 | let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; |
85aaf69f SL |
506 | Ok(n as u64) |
507 | } | |
508 | ||
7453a54e SL |
509 | pub fn duplicate(&self) -> io::Result<File> { |
510 | self.0.duplicate().map(File) | |
511 | } | |
512 | ||
85aaf69f | 513 | pub fn fd(&self) -> &FileDesc { &self.0 } |
c1a9b12d SL |
514 | |
515 | pub fn into_fd(self) -> FileDesc { self.0 } | |
85aaf69f SL |
516 | } |
517 | ||
d9579d0f AL |
518 | impl DirBuilder { |
519 | pub fn new() -> DirBuilder { | |
520 | DirBuilder { mode: 0o777 } | |
521 | } | |
522 | ||
523 | pub fn mkdir(&self, p: &Path) -> io::Result<()> { | |
54a0048b SL |
524 | let p = cstr(p)?; |
525 | cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; | |
d9579d0f AL |
526 | Ok(()) |
527 | } | |
528 | ||
7453a54e SL |
529 | pub fn set_mode(&mut self, mode: u32) { |
530 | self.mode = mode as mode_t; | |
d9579d0f AL |
531 | } |
532 | } | |
533 | ||
85aaf69f | 534 | fn cstr(path: &Path) -> io::Result<CString> { |
54a0048b | 535 | Ok(CString::new(path.as_os_str().as_bytes())?) |
c34b1796 AL |
536 | } |
537 | ||
538 | impl FromInner<c_int> for File { | |
539 | fn from_inner(fd: c_int) -> File { | |
540 | File(FileDesc::new(fd)) | |
541 | } | |
85aaf69f SL |
542 | } |
543 | ||
d9579d0f AL |
544 | impl fmt::Debug for File { |
545 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
546 | #[cfg(target_os = "linux")] | |
547 | fn get_path(fd: c_int) -> Option<PathBuf> { | |
548 | use string::ToString; | |
549 | let mut p = PathBuf::from("/proc/self/fd"); | |
550 | p.push(&fd.to_string()); | |
551 | readlink(&p).ok() | |
552 | } | |
553 | ||
c1a9b12d SL |
554 | #[cfg(target_os = "macos")] |
555 | fn get_path(fd: c_int) -> Option<PathBuf> { | |
e9174d1e SL |
556 | // FIXME: The use of PATH_MAX is generally not encouraged, but it |
557 | // is inevitable in this case because OS X defines `fcntl` with | |
558 | // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no | |
559 | // alternatives. If a better method is invented, it should be used | |
560 | // instead. | |
c1a9b12d SL |
561 | let mut buf = vec![0;libc::PATH_MAX as usize]; |
562 | let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; | |
563 | if n == -1 { | |
564 | return None; | |
565 | } | |
566 | let l = buf.iter().position(|&c| c == 0).unwrap(); | |
567 | buf.truncate(l as usize); | |
e9174d1e | 568 | buf.shrink_to_fit(); |
c1a9b12d SL |
569 | Some(PathBuf::from(OsString::from_vec(buf))) |
570 | } | |
571 | ||
572 | #[cfg(not(any(target_os = "linux", target_os = "macos")))] | |
d9579d0f AL |
573 | fn get_path(_fd: c_int) -> Option<PathBuf> { |
574 | // FIXME(#24570): implement this for other Unix platforms | |
575 | None | |
576 | } | |
577 | ||
c1a9b12d | 578 | #[cfg(any(target_os = "linux", target_os = "macos"))] |
d9579d0f AL |
579 | fn get_mode(fd: c_int) -> Option<(bool, bool)> { |
580 | let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; | |
581 | if mode == -1 { | |
582 | return None; | |
583 | } | |
584 | match mode & libc::O_ACCMODE { | |
585 | libc::O_RDONLY => Some((true, false)), | |
586 | libc::O_RDWR => Some((true, true)), | |
587 | libc::O_WRONLY => Some((false, true)), | |
588 | _ => None | |
589 | } | |
590 | } | |
591 | ||
c1a9b12d | 592 | #[cfg(not(any(target_os = "linux", target_os = "macos")))] |
d9579d0f AL |
593 | fn get_mode(_fd: c_int) -> Option<(bool, bool)> { |
594 | // FIXME(#24570): implement this for other Unix platforms | |
595 | None | |
596 | } | |
597 | ||
598 | let fd = self.0.raw(); | |
62682a34 SL |
599 | let mut b = f.debug_struct("File"); |
600 | b.field("fd", &fd); | |
d9579d0f | 601 | if let Some(path) = get_path(fd) { |
62682a34 | 602 | b.field("path", &path); |
d9579d0f AL |
603 | } |
604 | if let Some((read, write)) = get_mode(fd) { | |
62682a34 | 605 | b.field("read", &read).field("write", &write); |
d9579d0f AL |
606 | } |
607 | b.finish() | |
608 | } | |
85aaf69f SL |
609 | } |
610 | ||
611 | pub fn readdir(p: &Path) -> io::Result<ReadDir> { | |
c34b1796 | 612 | let root = Arc::new(p.to_path_buf()); |
54a0048b | 613 | let p = cstr(p)?; |
85aaf69f SL |
614 | unsafe { |
615 | let ptr = libc::opendir(p.as_ptr()); | |
616 | if ptr.is_null() { | |
617 | Err(Error::last_os_error()) | |
618 | } else { | |
c34b1796 | 619 | Ok(ReadDir { dirp: Dir(ptr), root: root }) |
85aaf69f SL |
620 | } |
621 | } | |
622 | } | |
623 | ||
624 | pub fn unlink(p: &Path) -> io::Result<()> { | |
54a0048b SL |
625 | let p = cstr(p)?; |
626 | cvt(unsafe { libc::unlink(p.as_ptr()) })?; | |
85aaf69f SL |
627 | Ok(()) |
628 | } | |
629 | ||
630 | pub fn rename(old: &Path, new: &Path) -> io::Result<()> { | |
54a0048b SL |
631 | let old = cstr(old)?; |
632 | let new = cstr(new)?; | |
633 | cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; | |
85aaf69f SL |
634 | Ok(()) |
635 | } | |
636 | ||
637 | pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { | |
54a0048b SL |
638 | let p = cstr(p)?; |
639 | cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; | |
85aaf69f SL |
640 | Ok(()) |
641 | } | |
642 | ||
643 | pub fn rmdir(p: &Path) -> io::Result<()> { | |
54a0048b SL |
644 | let p = cstr(p)?; |
645 | cvt(unsafe { libc::rmdir(p.as_ptr()) })?; | |
85aaf69f SL |
646 | Ok(()) |
647 | } | |
648 | ||
7453a54e | 649 | pub fn remove_dir_all(path: &Path) -> io::Result<()> { |
54a0048b | 650 | let filetype = lstat(path)?.file_type(); |
7453a54e SL |
651 | if filetype.is_symlink() { |
652 | unlink(path) | |
653 | } else { | |
654 | remove_dir_all_recursive(path) | |
655 | } | |
656 | } | |
657 | ||
658 | fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { | |
54a0048b SL |
659 | for child in readdir(path)? { |
660 | let child = child?; | |
661 | if child.file_type()?.is_dir() { | |
662 | remove_dir_all_recursive(&child.path())?; | |
7453a54e | 663 | } else { |
54a0048b | 664 | unlink(&child.path())?; |
7453a54e SL |
665 | } |
666 | } | |
667 | rmdir(path) | |
668 | } | |
669 | ||
85aaf69f | 670 | pub fn readlink(p: &Path) -> io::Result<PathBuf> { |
54a0048b | 671 | let c_path = cstr(p)?; |
85aaf69f | 672 | let p = c_path.as_ptr(); |
e9174d1e SL |
673 | |
674 | let mut buf = Vec::with_capacity(256); | |
675 | ||
676 | loop { | |
54a0048b | 677 | let buf_read = cvt(unsafe { |
e9174d1e | 678 | libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity() as libc::size_t) |
54a0048b | 679 | })? as usize; |
e9174d1e SL |
680 | |
681 | unsafe { buf.set_len(buf_read); } | |
682 | ||
683 | if buf_read != buf.capacity() { | |
684 | buf.shrink_to_fit(); | |
685 | ||
686 | return Ok(PathBuf::from(OsString::from_vec(buf))); | |
687 | } | |
688 | ||
689 | // Trigger the internal buffer resizing logic of `Vec` by requiring | |
690 | // more space than the current capacity. The length is guaranteed to be | |
691 | // the same as the capacity due to the if statement above. | |
692 | buf.reserve(1); | |
85aaf69f | 693 | } |
85aaf69f SL |
694 | } |
695 | ||
696 | pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { | |
54a0048b SL |
697 | let src = cstr(src)?; |
698 | let dst = cstr(dst)?; | |
699 | cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; | |
85aaf69f SL |
700 | Ok(()) |
701 | } | |
702 | ||
703 | pub fn link(src: &Path, dst: &Path) -> io::Result<()> { | |
54a0048b SL |
704 | let src = cstr(src)?; |
705 | let dst = cstr(dst)?; | |
706 | cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; | |
85aaf69f SL |
707 | Ok(()) |
708 | } | |
709 | ||
710 | pub fn stat(p: &Path) -> io::Result<FileAttr> { | |
54a0048b | 711 | let p = cstr(p)?; |
7453a54e | 712 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
54a0048b | 713 | cvt(unsafe { |
7453a54e | 714 | stat64(p.as_ptr(), &mut stat as *mut _ as *mut _) |
54a0048b | 715 | })?; |
85aaf69f SL |
716 | Ok(FileAttr { stat: stat }) |
717 | } | |
718 | ||
719 | pub fn lstat(p: &Path) -> io::Result<FileAttr> { | |
54a0048b | 720 | let p = cstr(p)?; |
7453a54e | 721 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
54a0048b | 722 | cvt(unsafe { |
7453a54e | 723 | lstat64(p.as_ptr(), &mut stat as *mut _ as *mut _) |
54a0048b | 724 | })?; |
85aaf69f SL |
725 | Ok(FileAttr { stat: stat }) |
726 | } | |
727 | ||
d9579d0f | 728 | pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { |
54a0048b | 729 | let path = CString::new(p.as_os_str().as_bytes())?; |
e9174d1e | 730 | let buf; |
d9579d0f | 731 | unsafe { |
92a42be0 | 732 | let r = libc::realpath(path.as_ptr(), ptr::null_mut()); |
d9579d0f AL |
733 | if r.is_null() { |
734 | return Err(io::Error::last_os_error()) | |
735 | } | |
e9174d1e SL |
736 | buf = CStr::from_ptr(r).to_bytes().to_vec(); |
737 | libc::free(r as *mut _); | |
d9579d0f | 738 | } |
d9579d0f AL |
739 | Ok(PathBuf::from(OsString::from_vec(buf))) |
740 | } | |
c1a9b12d SL |
741 | |
742 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { | |
b039eaaf | 743 | use fs::{File, set_permissions}; |
c1a9b12d SL |
744 | if !from.is_file() { |
745 | return Err(Error::new(ErrorKind::InvalidInput, | |
e9174d1e | 746 | "the source path is not an existing regular file")) |
c1a9b12d SL |
747 | } |
748 | ||
54a0048b SL |
749 | let mut reader = File::open(from)?; |
750 | let mut writer = File::create(to)?; | |
751 | let perm = reader.metadata()?.permissions(); | |
c1a9b12d | 752 | |
54a0048b SL |
753 | let ret = io::copy(&mut reader, &mut writer)?; |
754 | set_permissions(to, perm)?; | |
c1a9b12d SL |
755 | Ok(ret) |
756 | } |