]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2015 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 | ||
54a0048b | 11 | //! Filesystem manipulation operations. |
85aaf69f SL |
12 | //! |
13 | //! This module contains basic methods to manipulate the contents of the local | |
14 | //! filesystem. All methods in this module represent cross-platform filesystem | |
15 | //! operations. Extra platform-specific functionality can be found in the | |
16 | //! extension traits of `std::os::$platform`. | |
17 | ||
c34b1796 | 18 | #![stable(feature = "rust1", since = "1.0.0")] |
85aaf69f | 19 | |
d9579d0f AL |
20 | use fmt; |
21 | use ffi::OsString; | |
c1a9b12d | 22 | use io::{self, SeekFrom, Seek, Read, Write}; |
c34b1796 | 23 | use path::{Path, PathBuf}; |
d9579d0f | 24 | use sys::fs as fs_imp; |
c1a9b12d | 25 | use sys_common::{AsInnerMut, FromInner, AsInner, IntoInner}; |
85aaf69f | 26 | use vec::Vec; |
7453a54e | 27 | use time::SystemTime; |
85aaf69f SL |
28 | |
29 | /// A reference to an open file on the filesystem. | |
30 | /// | |
31 | /// An instance of a `File` can be read and/or written depending on what options | |
32 | /// it was opened with. Files also implement `Seek` to alter the logical cursor | |
33 | /// that the file contains internally. | |
34 | /// | |
c34b1796 | 35 | /// # Examples |
85aaf69f SL |
36 | /// |
37 | /// ```no_run | |
38 | /// use std::io::prelude::*; | |
39 | /// use std::fs::File; | |
40 | /// | |
41 | /// # fn foo() -> std::io::Result<()> { | |
42 | /// let mut f = try!(File::create("foo.txt")); | |
43 | /// try!(f.write_all(b"Hello, world!")); | |
44 | /// | |
45 | /// let mut f = try!(File::open("foo.txt")); | |
46 | /// let mut s = String::new(); | |
47 | /// try!(f.read_to_string(&mut s)); | |
48 | /// assert_eq!(s, "Hello, world!"); | |
49 | /// # Ok(()) | |
50 | /// # } | |
51 | /// ``` | |
c34b1796 | 52 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
53 | pub struct File { |
54 | inner: fs_imp::File, | |
85aaf69f SL |
55 | } |
56 | ||
57 | /// Metadata information about a file. | |
58 | /// | |
59 | /// This structure is returned from the `metadata` function or method and | |
60 | /// represents known metadata about a file such as its permissions, size, | |
61 | /// modification times, etc. | |
c34b1796 | 62 | #[stable(feature = "rust1", since = "1.0.0")] |
b039eaaf | 63 | #[derive(Clone)] |
85aaf69f SL |
64 | pub struct Metadata(fs_imp::FileAttr); |
65 | ||
66 | /// Iterator over the entries in a directory. | |
67 | /// | |
68 | /// This iterator is returned from the `read_dir` function of this module and | |
69 | /// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry` | |
70 | /// information like the entry's path and possibly other metadata can be | |
71 | /// learned. | |
c34b1796 | 72 | /// |
7453a54e | 73 | /// # Errors |
c34b1796 AL |
74 | /// |
75 | /// This `io::Result` will be an `Err` if there's some sort of intermittent | |
76 | /// IO error during iteration. | |
77 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
78 | pub struct ReadDir(fs_imp::ReadDir); |
79 | ||
80 | /// Entries returned by the `ReadDir` iterator. | |
81 | /// | |
82 | /// An instance of `DirEntry` represents an entry inside of a directory on the | |
83 | /// filesystem. Each entry can be inspected via methods to learn about the full | |
84 | /// path or possibly other metadata through per-platform extension traits. | |
c34b1796 | 85 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
86 | pub struct DirEntry(fs_imp::DirEntry); |
87 | ||
85aaf69f SL |
88 | /// Options and flags which can be used to configure how a file is opened. |
89 | /// | |
90 | /// This builder exposes the ability to configure how a `File` is opened and | |
91 | /// what operations are permitted on the open file. The `File::open` and | |
92 | /// `File::create` methods are aliases for commonly used options using this | |
93 | /// builder. | |
9346a6ac AL |
94 | /// |
95 | /// Generally speaking, when using `OpenOptions`, you'll first call `new()`, | |
96 | /// then chain calls to methods to set each option, then call `open()`, passing | |
97 | /// the path of the file you're trying to open. This will give you a | |
98 | /// [`io::Result`][result] with a [`File`][file] inside that you can further | |
99 | /// operate on. | |
100 | /// | |
101 | /// [result]: ../io/type.Result.html | |
102 | /// [file]: struct.File.html | |
103 | /// | |
104 | /// # Examples | |
105 | /// | |
106 | /// Opening a file to read: | |
107 | /// | |
108 | /// ```no_run | |
109 | /// use std::fs::OpenOptions; | |
110 | /// | |
111 | /// let file = OpenOptions::new().read(true).open("foo.txt"); | |
112 | /// ``` | |
113 | /// | |
114 | /// Opening a file for both reading and writing, as well as creating it if it | |
115 | /// doesn't exist: | |
116 | /// | |
117 | /// ```no_run | |
118 | /// use std::fs::OpenOptions; | |
119 | /// | |
120 | /// let file = OpenOptions::new() | |
121 | /// .read(true) | |
122 | /// .write(true) | |
123 | /// .create(true) | |
124 | /// .open("foo.txt"); | |
125 | /// ``` | |
85aaf69f | 126 | #[derive(Clone)] |
c34b1796 | 127 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
128 | pub struct OpenOptions(fs_imp::OpenOptions); |
129 | ||
130 | /// Representation of the various permissions on a file. | |
131 | /// | |
132 | /// This module only currently provides one bit of information, `readonly`, | |
133 | /// which is exposed on all currently supported platforms. Unix-specific | |
134 | /// functionality, such as mode bits, is available through the | |
135 | /// `os::unix::PermissionsExt` trait. | |
136 | #[derive(Clone, PartialEq, Eq, Debug)] | |
c34b1796 | 137 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
138 | pub struct Permissions(fs_imp::FilePermissions); |
139 | ||
d9579d0f AL |
140 | /// An structure representing a type of file with accessors for each file type. |
141 | #[stable(feature = "file_type", since = "1.1.0")] | |
142 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | |
143 | pub struct FileType(fs_imp::FileType); | |
144 | ||
145 | /// A builder used to create directories in various manners. | |
146 | /// | |
147 | /// This builder also supports platform-specific options. | |
92a42be0 | 148 | #[stable(feature = "dir_builder", since = "1.6.0")] |
d9579d0f AL |
149 | pub struct DirBuilder { |
150 | inner: fs_imp::DirBuilder, | |
151 | recursive: bool, | |
152 | } | |
153 | ||
85aaf69f SL |
154 | impl File { |
155 | /// Attempts to open a file in read-only mode. | |
156 | /// | |
157 | /// See the `OpenOptions::open` method for more details. | |
158 | /// | |
159 | /// # Errors | |
160 | /// | |
161 | /// This function will return an error if `path` does not already exist. | |
162 | /// Other errors may also be returned according to `OpenOptions::open`. | |
c34b1796 AL |
163 | /// |
164 | /// # Examples | |
165 | /// | |
166 | /// ```no_run | |
167 | /// use std::fs::File; | |
168 | /// | |
169 | /// # fn foo() -> std::io::Result<()> { | |
170 | /// let mut f = try!(File::open("foo.txt")); | |
171 | /// # Ok(()) | |
172 | /// # } | |
173 | /// ``` | |
174 | #[stable(feature = "rust1", since = "1.0.0")] | |
175 | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
e9174d1e | 176 | OpenOptions::new().read(true).open(path.as_ref()) |
85aaf69f SL |
177 | } |
178 | ||
9346a6ac | 179 | /// Opens a file in write-only mode. |
85aaf69f SL |
180 | /// |
181 | /// This function will create a file if it does not exist, | |
182 | /// and will truncate it if it does. | |
183 | /// | |
184 | /// See the `OpenOptions::open` function for more details. | |
c34b1796 AL |
185 | /// |
186 | /// # Examples | |
187 | /// | |
188 | /// ```no_run | |
189 | /// use std::fs::File; | |
190 | /// | |
191 | /// # fn foo() -> std::io::Result<()> { | |
192 | /// let mut f = try!(File::create("foo.txt")); | |
193 | /// # Ok(()) | |
194 | /// # } | |
195 | /// ``` | |
196 | #[stable(feature = "rust1", since = "1.0.0")] | |
197 | pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
e9174d1e | 198 | OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) |
85aaf69f SL |
199 | } |
200 | ||
9346a6ac | 201 | /// Attempts to sync all OS-internal metadata to disk. |
85aaf69f SL |
202 | /// |
203 | /// This function will attempt to ensure that all in-core data reaches the | |
204 | /// filesystem before returning. | |
c34b1796 AL |
205 | /// |
206 | /// # Examples | |
207 | /// | |
208 | /// ```no_run | |
209 | /// use std::fs::File; | |
210 | /// use std::io::prelude::*; | |
211 | /// | |
212 | /// # fn foo() -> std::io::Result<()> { | |
213 | /// let mut f = try!(File::create("foo.txt")); | |
214 | /// try!(f.write_all(b"Hello, world!")); | |
215 | /// | |
216 | /// try!(f.sync_all()); | |
217 | /// # Ok(()) | |
218 | /// # } | |
219 | /// ``` | |
220 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
221 | pub fn sync_all(&self) -> io::Result<()> { |
222 | self.inner.fsync() | |
223 | } | |
224 | ||
225 | /// This function is similar to `sync_all`, except that it may not | |
226 | /// synchronize file metadata to the filesystem. | |
227 | /// | |
228 | /// This is intended for use cases that must synchronize content, but don't | |
229 | /// need the metadata on disk. The goal of this method is to reduce disk | |
230 | /// operations. | |
231 | /// | |
232 | /// Note that some platforms may simply implement this in terms of | |
233 | /// `sync_all`. | |
c34b1796 AL |
234 | /// |
235 | /// # Examples | |
236 | /// | |
237 | /// ```no_run | |
238 | /// use std::fs::File; | |
239 | /// use std::io::prelude::*; | |
240 | /// | |
241 | /// # fn foo() -> std::io::Result<()> { | |
242 | /// let mut f = try!(File::create("foo.txt")); | |
243 | /// try!(f.write_all(b"Hello, world!")); | |
244 | /// | |
245 | /// try!(f.sync_data()); | |
246 | /// # Ok(()) | |
247 | /// # } | |
248 | /// ``` | |
249 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
250 | pub fn sync_data(&self) -> io::Result<()> { |
251 | self.inner.datasync() | |
252 | } | |
253 | ||
254 | /// Truncates or extends the underlying file, updating the size of | |
255 | /// this file to become `size`. | |
256 | /// | |
257 | /// If the `size` is less than the current file's size, then the file will | |
258 | /// be shrunk. If it is greater than the current file's size, then the file | |
259 | /// will be extended to `size` and have all of the intermediate data filled | |
260 | /// in with 0s. | |
c34b1796 | 261 | /// |
c1a9b12d SL |
262 | /// # Errors |
263 | /// | |
264 | /// This function will return an error if the file is not opened for writing. | |
265 | /// | |
c34b1796 AL |
266 | /// # Examples |
267 | /// | |
268 | /// ```no_run | |
269 | /// use std::fs::File; | |
270 | /// | |
271 | /// # fn foo() -> std::io::Result<()> { | |
c1a9b12d SL |
272 | /// let mut f = try!(File::create("foo.txt")); |
273 | /// try!(f.set_len(10)); | |
c34b1796 AL |
274 | /// # Ok(()) |
275 | /// # } | |
276 | /// ``` | |
277 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
278 | pub fn set_len(&self, size: u64) -> io::Result<()> { |
279 | self.inner.truncate(size) | |
280 | } | |
281 | ||
c34b1796 AL |
282 | /// Queries metadata about the underlying file. |
283 | /// | |
284 | /// # Examples | |
285 | /// | |
286 | /// ```no_run | |
287 | /// use std::fs::File; | |
288 | /// | |
289 | /// # fn foo() -> std::io::Result<()> { | |
290 | /// let mut f = try!(File::open("foo.txt")); | |
291 | /// let metadata = try!(f.metadata()); | |
292 | /// # Ok(()) | |
293 | /// # } | |
294 | /// ``` | |
295 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
296 | pub fn metadata(&self) -> io::Result<Metadata> { |
297 | self.inner.file_attr().map(Metadata) | |
298 | } | |
7453a54e SL |
299 | |
300 | /// Creates a new independently owned handle to the underlying file. | |
301 | /// | |
302 | /// The returned `File` is a reference to the same state that this object | |
303 | /// references. Both handles will read and write with the same cursor | |
304 | /// position. | |
54a0048b | 305 | #[stable(feature = "file_try_clone", since = "1.9.0")] |
7453a54e SL |
306 | pub fn try_clone(&self) -> io::Result<File> { |
307 | Ok(File { | |
54a0048b | 308 | inner: self.inner.duplicate()? |
7453a54e SL |
309 | }) |
310 | } | |
85aaf69f SL |
311 | } |
312 | ||
313 | impl AsInner<fs_imp::File> for File { | |
314 | fn as_inner(&self) -> &fs_imp::File { &self.inner } | |
315 | } | |
c34b1796 AL |
316 | impl FromInner<fs_imp::File> for File { |
317 | fn from_inner(f: fs_imp::File) -> File { | |
d9579d0f AL |
318 | File { inner: f } |
319 | } | |
320 | } | |
c1a9b12d SL |
321 | impl IntoInner<fs_imp::File> for File { |
322 | fn into_inner(self) -> fs_imp::File { | |
323 | self.inner | |
324 | } | |
325 | } | |
d9579d0f | 326 | |
92a42be0 | 327 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f AL |
328 | impl fmt::Debug for File { |
329 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
330 | self.inner.fmt(f) | |
c34b1796 AL |
331 | } |
332 | } | |
333 | ||
334 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
335 | impl Read for File { |
336 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
337 | self.inner.read(buf) | |
338 | } | |
c1a9b12d | 339 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
54a0048b | 340 | self.inner.read_to_end(buf) |
c1a9b12d | 341 | } |
85aaf69f | 342 | } |
c34b1796 | 343 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
344 | impl Write for File { |
345 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
346 | self.inner.write(buf) | |
347 | } | |
348 | fn flush(&mut self) -> io::Result<()> { self.inner.flush() } | |
349 | } | |
c34b1796 | 350 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
351 | impl Seek for File { |
352 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { | |
353 | self.inner.seek(pos) | |
354 | } | |
355 | } | |
c34b1796 | 356 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
357 | impl<'a> Read for &'a File { |
358 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
359 | self.inner.read(buf) | |
360 | } | |
54a0048b SL |
361 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
362 | self.inner.read_to_end(buf) | |
363 | } | |
85aaf69f | 364 | } |
c34b1796 | 365 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
366 | impl<'a> Write for &'a File { |
367 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
368 | self.inner.write(buf) | |
369 | } | |
370 | fn flush(&mut self) -> io::Result<()> { self.inner.flush() } | |
371 | } | |
c34b1796 | 372 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
373 | impl<'a> Seek for &'a File { |
374 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { | |
375 | self.inner.seek(pos) | |
376 | } | |
377 | } | |
378 | ||
379 | impl OpenOptions { | |
7453a54e | 380 | /// Creates a blank new set of options ready for configuration. |
85aaf69f SL |
381 | /// |
382 | /// All options are initially set to `false`. | |
9346a6ac AL |
383 | /// |
384 | /// # Examples | |
385 | /// | |
386 | /// ```no_run | |
387 | /// use std::fs::OpenOptions; | |
388 | /// | |
7453a54e SL |
389 | /// let mut options = OpenOptions::new(); |
390 | /// let file = options.read(true).open("foo.txt"); | |
9346a6ac | 391 | /// ``` |
c34b1796 | 392 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
393 | pub fn new() -> OpenOptions { |
394 | OpenOptions(fs_imp::OpenOptions::new()) | |
395 | } | |
396 | ||
9346a6ac | 397 | /// Sets the option for read access. |
85aaf69f SL |
398 | /// |
399 | /// This option, when true, will indicate that the file should be | |
400 | /// `read`-able if opened. | |
9346a6ac AL |
401 | /// |
402 | /// # Examples | |
403 | /// | |
404 | /// ```no_run | |
405 | /// use std::fs::OpenOptions; | |
406 | /// | |
407 | /// let file = OpenOptions::new().read(true).open("foo.txt"); | |
408 | /// ``` | |
c34b1796 | 409 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
410 | pub fn read(&mut self, read: bool) -> &mut OpenOptions { |
411 | self.0.read(read); self | |
412 | } | |
413 | ||
9346a6ac | 414 | /// Sets the option for write access. |
85aaf69f SL |
415 | /// |
416 | /// This option, when true, will indicate that the file should be | |
417 | /// `write`-able if opened. | |
9346a6ac | 418 | /// |
7453a54e SL |
419 | /// If the file already exists, any write calls on it will overwrite its |
420 | /// contents, without truncating it. | |
421 | /// | |
9346a6ac AL |
422 | /// # Examples |
423 | /// | |
424 | /// ```no_run | |
425 | /// use std::fs::OpenOptions; | |
426 | /// | |
427 | /// let file = OpenOptions::new().write(true).open("foo.txt"); | |
428 | /// ``` | |
c34b1796 | 429 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
430 | pub fn write(&mut self, write: bool) -> &mut OpenOptions { |
431 | self.0.write(write); self | |
432 | } | |
433 | ||
9346a6ac | 434 | /// Sets the option for the append mode. |
85aaf69f SL |
435 | /// |
436 | /// This option, when true, means that writes will append to a file instead | |
437 | /// of overwriting previous contents. | |
7453a54e SL |
438 | /// Note that setting `.write(true).append(true)` has the same effect as |
439 | /// setting only `.append(true)`. | |
440 | /// | |
441 | /// For most filesystems, the operating system guarantees that all writes are | |
442 | /// atomic: no writes get mangled because another process writes at the same | |
443 | /// time. | |
444 | /// | |
445 | /// One maybe obvious note when using append-mode: make sure that all data | |
446 | /// that belongs together is written to the file in one operation. This | |
447 | /// can be done by concatenating strings before passing them to `write()`, | |
448 | /// or using a buffered writer (with a buffer of adequate size), | |
449 | /// and calling `flush()` when the message is complete. | |
450 | /// | |
451 | /// If a file is opened with both read and append access, beware that after | |
452 | /// opening, and after every write, the position for reading may be set at the | |
453 | /// end of the file. So, before writing, save the current position (using | |
454 | /// `seek(SeekFrom::Current(0))`, and restore it before the next read. | |
9346a6ac AL |
455 | /// |
456 | /// # Examples | |
457 | /// | |
458 | /// ```no_run | |
459 | /// use std::fs::OpenOptions; | |
460 | /// | |
7453a54e | 461 | /// let file = OpenOptions::new().append(true).open("foo.txt"); |
9346a6ac | 462 | /// ``` |
c34b1796 | 463 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
464 | pub fn append(&mut self, append: bool) -> &mut OpenOptions { |
465 | self.0.append(append); self | |
466 | } | |
467 | ||
9346a6ac | 468 | /// Sets the option for truncating a previous file. |
85aaf69f SL |
469 | /// |
470 | /// If a file is successfully opened with this option set it will truncate | |
471 | /// the file to 0 length if it already exists. | |
9346a6ac | 472 | /// |
7453a54e SL |
473 | /// The file must be opened with write access for truncate to work. |
474 | /// | |
9346a6ac AL |
475 | /// # Examples |
476 | /// | |
477 | /// ```no_run | |
478 | /// use std::fs::OpenOptions; | |
479 | /// | |
c1a9b12d | 480 | /// let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); |
9346a6ac | 481 | /// ``` |
c34b1796 | 482 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
483 | pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { |
484 | self.0.truncate(truncate); self | |
485 | } | |
486 | ||
9346a6ac | 487 | /// Sets the option for creating a new file. |
85aaf69f SL |
488 | /// |
489 | /// This option indicates whether a new file will be created if the file | |
490 | /// does not yet already exist. | |
9346a6ac | 491 | /// |
7453a54e SL |
492 | /// In order for the file to be created, `write` or `append` access must |
493 | /// be used. | |
494 | /// | |
9346a6ac AL |
495 | /// # Examples |
496 | /// | |
497 | /// ```no_run | |
498 | /// use std::fs::OpenOptions; | |
499 | /// | |
7453a54e | 500 | /// let file = OpenOptions::new().write(true).create(true).open("foo.txt"); |
9346a6ac | 501 | /// ``` |
c34b1796 | 502 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
503 | pub fn create(&mut self, create: bool) -> &mut OpenOptions { |
504 | self.0.create(create); self | |
505 | } | |
506 | ||
7453a54e SL |
507 | /// Sets the option to always create a new file. |
508 | /// | |
509 | /// This option indicates whether a new file will be created. | |
510 | /// No file is allowed to exist at the target location, also no (dangling) | |
511 | /// symlink. | |
512 | /// | |
513 | /// This option is useful because it as atomic. Otherwise between checking | |
514 | /// whether a file exists and creating a new one, the file may have been | |
515 | /// created by another process (a TOCTOU race condition / attack). | |
516 | /// | |
517 | /// If `.create_new(true)` is set, `.create()` and `.truncate()` are | |
518 | /// ignored. | |
519 | /// | |
520 | /// The file must be opened with write or append access in order to create | |
521 | /// a new file. | |
522 | /// | |
523 | /// # Examples | |
524 | /// | |
525 | /// ```no_run | |
7453a54e SL |
526 | /// use std::fs::OpenOptions; |
527 | /// | |
528 | /// let file = OpenOptions::new().write(true) | |
529 | /// .create_new(true) | |
530 | /// .open("foo.txt"); | |
531 | /// ``` | |
54a0048b | 532 | #[stable(feature = "expand_open_options2", since = "1.9.0")] |
7453a54e SL |
533 | pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { |
534 | self.0.create_new(create_new); self | |
535 | } | |
536 | ||
9346a6ac | 537 | /// Opens a file at `path` with the options specified by `self`. |
85aaf69f SL |
538 | /// |
539 | /// # Errors | |
540 | /// | |
541 | /// This function will return an error under a number of different | |
542 | /// circumstances, to include but not limited to: | |
543 | /// | |
7453a54e SL |
544 | /// * Opening a file that does not exist without setting `create` or |
545 | /// `create_new`. | |
85aaf69f SL |
546 | /// * Attempting to open a file with access that the user lacks |
547 | /// permissions for | |
548 | /// * Filesystem-level errors (full disk, etc) | |
7453a54e SL |
549 | /// * Invalid combinations of open options (truncate without write access, |
550 | /// no access mode set, etc) | |
9346a6ac AL |
551 | /// |
552 | /// # Examples | |
553 | /// | |
554 | /// ```no_run | |
555 | /// use std::fs::OpenOptions; | |
556 | /// | |
557 | /// let file = OpenOptions::new().open("foo.txt"); | |
558 | /// ``` | |
c34b1796 AL |
559 | #[stable(feature = "rust1", since = "1.0.0")] |
560 | pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> { | |
e9174d1e SL |
561 | self._open(path.as_ref()) |
562 | } | |
563 | ||
564 | fn _open(&self, path: &Path) -> io::Result<File> { | |
54a0048b | 565 | let inner = fs_imp::File::open(path, &self.0)?; |
d9579d0f | 566 | Ok(File { inner: inner }) |
85aaf69f SL |
567 | } |
568 | } | |
c34b1796 | 569 | |
85aaf69f SL |
570 | impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { |
571 | fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } | |
572 | } | |
573 | ||
574 | impl Metadata { | |
d9579d0f AL |
575 | /// Returns the file type for this metadata. |
576 | #[stable(feature = "file_type", since = "1.1.0")] | |
577 | pub fn file_type(&self) -> FileType { | |
578 | FileType(self.0.file_type()) | |
579 | } | |
580 | ||
85aaf69f | 581 | /// Returns whether this metadata is for a directory. |
9346a6ac AL |
582 | /// |
583 | /// # Examples | |
584 | /// | |
585 | /// ``` | |
586 | /// # fn foo() -> std::io::Result<()> { | |
587 | /// use std::fs; | |
588 | /// | |
589 | /// let metadata = try!(fs::metadata("foo.txt")); | |
590 | /// | |
591 | /// assert!(!metadata.is_dir()); | |
592 | /// # Ok(()) | |
593 | /// # } | |
594 | /// ``` | |
c34b1796 | 595 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 596 | pub fn is_dir(&self) -> bool { self.file_type().is_dir() } |
85aaf69f SL |
597 | |
598 | /// Returns whether this metadata is for a regular file. | |
9346a6ac AL |
599 | /// |
600 | /// # Examples | |
601 | /// | |
602 | /// ``` | |
603 | /// # fn foo() -> std::io::Result<()> { | |
604 | /// use std::fs; | |
605 | /// | |
606 | /// let metadata = try!(fs::metadata("foo.txt")); | |
607 | /// | |
608 | /// assert!(metadata.is_file()); | |
609 | /// # Ok(()) | |
610 | /// # } | |
611 | /// ``` | |
c34b1796 | 612 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 613 | pub fn is_file(&self) -> bool { self.file_type().is_file() } |
85aaf69f SL |
614 | |
615 | /// Returns the size of the file, in bytes, this metadata is for. | |
9346a6ac AL |
616 | /// |
617 | /// # Examples | |
618 | /// | |
619 | /// ``` | |
620 | /// # fn foo() -> std::io::Result<()> { | |
621 | /// use std::fs; | |
622 | /// | |
623 | /// let metadata = try!(fs::metadata("foo.txt")); | |
624 | /// | |
625 | /// assert_eq!(0, metadata.len()); | |
626 | /// # Ok(()) | |
627 | /// # } | |
628 | /// ``` | |
c34b1796 | 629 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
630 | pub fn len(&self) -> u64 { self.0.size() } |
631 | ||
632 | /// Returns the permissions of the file this metadata is for. | |
9346a6ac AL |
633 | /// |
634 | /// # Examples | |
635 | /// | |
636 | /// ``` | |
637 | /// # fn foo() -> std::io::Result<()> { | |
638 | /// use std::fs; | |
639 | /// | |
640 | /// let metadata = try!(fs::metadata("foo.txt")); | |
641 | /// | |
642 | /// assert!(!metadata.permissions().readonly()); | |
643 | /// # Ok(()) | |
644 | /// # } | |
645 | /// ``` | |
c34b1796 | 646 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
647 | pub fn permissions(&self) -> Permissions { |
648 | Permissions(self.0.perm()) | |
649 | } | |
7453a54e SL |
650 | |
651 | /// Returns the last modification time listed in this metadata. | |
652 | /// | |
653 | /// The returned value corresponds to the `mtime` field of `stat` on Unix | |
654 | /// platforms and the `ftLastWriteTime` field on Windows platforms. | |
655 | /// | |
656 | /// # Errors | |
657 | /// | |
658 | /// This field may not be available on all platforms, and will return an | |
659 | /// `Err` on platforms where it is not available. | |
660 | #[unstable(feature = "fs_time", issue = "31399")] | |
661 | pub fn modified(&self) -> io::Result<SystemTime> { | |
662 | self.0.modified().map(FromInner::from_inner) | |
663 | } | |
664 | ||
665 | /// Returns the last access time of this metadata. | |
666 | /// | |
667 | /// The returned value corresponds to the `atime` field of `stat` on Unix | |
668 | /// platforms and the `ftLastAccessTime` field on Windows platforms. | |
669 | /// | |
670 | /// Note that not all platforms will keep this field update in a file's | |
671 | /// metadata, for example Windows has an option to disable updating this | |
672 | /// time when files are accessed and Linux similarly has `noatime`. | |
673 | /// | |
674 | /// # Errors | |
675 | /// | |
676 | /// This field may not be available on all platforms, and will return an | |
677 | /// `Err` on platforms where it is not available. | |
678 | #[unstable(feature = "fs_time", issue = "31399")] | |
679 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
680 | self.0.accessed().map(FromInner::from_inner) | |
681 | } | |
682 | ||
683 | /// Returns the creation time listed in the this metadata. | |
684 | /// | |
685 | /// The returned value corresponds to the `birthtime` field of `stat` on | |
686 | /// Unix platforms and the `ftCreationTime` field on Windows platforms. | |
687 | /// | |
688 | /// # Errors | |
689 | /// | |
690 | /// This field may not be available on all platforms, and will return an | |
691 | /// `Err` on platforms where it is not available. | |
692 | #[unstable(feature = "fs_time", issue = "31399")] | |
693 | pub fn created(&self) -> io::Result<SystemTime> { | |
694 | self.0.created().map(FromInner::from_inner) | |
695 | } | |
d9579d0f | 696 | } |
85aaf69f | 697 | |
d9579d0f AL |
698 | impl AsInner<fs_imp::FileAttr> for Metadata { |
699 | fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } | |
85aaf69f SL |
700 | } |
701 | ||
702 | impl Permissions { | |
703 | /// Returns whether these permissions describe a readonly file. | |
9346a6ac AL |
704 | /// |
705 | /// # Examples | |
706 | /// | |
707 | /// ``` | |
708 | /// use std::fs::File; | |
709 | /// | |
710 | /// # fn foo() -> std::io::Result<()> { | |
711 | /// let mut f = try!(File::create("foo.txt")); | |
712 | /// let metadata = try!(f.metadata()); | |
713 | /// | |
714 | /// assert_eq!(false, metadata.permissions().readonly()); | |
715 | /// # Ok(()) | |
716 | /// # } | |
717 | /// ``` | |
c34b1796 | 718 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
719 | pub fn readonly(&self) -> bool { self.0.readonly() } |
720 | ||
9346a6ac | 721 | /// Modifies the readonly flag for this set of permissions. |
85aaf69f SL |
722 | /// |
723 | /// This operation does **not** modify the filesystem. To modify the | |
724 | /// filesystem use the `fs::set_permissions` function. | |
9346a6ac AL |
725 | /// |
726 | /// # Examples | |
727 | /// | |
728 | /// ``` | |
729 | /// use std::fs::File; | |
730 | /// | |
731 | /// # fn foo() -> std::io::Result<()> { | |
d9579d0f | 732 | /// let f = try!(File::create("foo.txt")); |
9346a6ac AL |
733 | /// let metadata = try!(f.metadata()); |
734 | /// let mut permissions = metadata.permissions(); | |
735 | /// | |
736 | /// permissions.set_readonly(true); | |
737 | /// | |
738 | /// // filesystem doesn't change | |
739 | /// assert_eq!(false, metadata.permissions().readonly()); | |
740 | /// | |
741 | /// // just this particular `permissions`. | |
742 | /// assert_eq!(true, permissions.readonly()); | |
743 | /// # Ok(()) | |
744 | /// # } | |
745 | /// ``` | |
c34b1796 | 746 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
747 | pub fn set_readonly(&mut self, readonly: bool) { |
748 | self.0.set_readonly(readonly) | |
749 | } | |
750 | } | |
751 | ||
d9579d0f AL |
752 | impl FileType { |
753 | /// Test whether this file type represents a directory. | |
754 | #[stable(feature = "file_type", since = "1.1.0")] | |
755 | pub fn is_dir(&self) -> bool { self.0.is_dir() } | |
756 | ||
757 | /// Test whether this file type represents a regular file. | |
758 | #[stable(feature = "file_type", since = "1.1.0")] | |
759 | pub fn is_file(&self) -> bool { self.0.is_file() } | |
760 | ||
761 | /// Test whether this file type represents a symbolic link. | |
762 | #[stable(feature = "file_type", since = "1.1.0")] | |
763 | pub fn is_symlink(&self) -> bool { self.0.is_symlink() } | |
764 | } | |
765 | ||
c1a9b12d SL |
766 | impl AsInner<fs_imp::FileType> for FileType { |
767 | fn as_inner(&self) -> &fs_imp::FileType { &self.0 } | |
768 | } | |
769 | ||
85aaf69f SL |
770 | impl FromInner<fs_imp::FilePermissions> for Permissions { |
771 | fn from_inner(f: fs_imp::FilePermissions) -> Permissions { | |
772 | Permissions(f) | |
773 | } | |
774 | } | |
775 | ||
c34b1796 AL |
776 | impl AsInner<fs_imp::FilePermissions> for Permissions { |
777 | fn as_inner(&self) -> &fs_imp::FilePermissions { &self.0 } | |
778 | } | |
779 | ||
780 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
781 | impl Iterator for ReadDir { |
782 | type Item = io::Result<DirEntry>; | |
783 | ||
784 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
785 | self.0.next().map(|entry| entry.map(DirEntry)) | |
786 | } | |
787 | } | |
788 | ||
789 | impl DirEntry { | |
790 | /// Returns the full path to the file that this entry represents. | |
791 | /// | |
792 | /// The full path is created by joining the original path to `read_dir` or | |
793 | /// `walk_dir` with the filename of this entry. | |
9346a6ac AL |
794 | /// |
795 | /// # Examples | |
796 | /// | |
797 | /// ``` | |
798 | /// use std::fs; | |
799 | /// # fn foo() -> std::io::Result<()> { | |
800 | /// for entry in try!(fs::read_dir(".")) { | |
801 | /// let dir = try!(entry); | |
802 | /// println!("{:?}", dir.path()); | |
803 | /// } | |
804 | /// # Ok(()) | |
805 | /// # } | |
806 | /// ``` | |
807 | /// | |
808 | /// This prints output like: | |
809 | /// | |
810 | /// ```text | |
811 | /// "./whatever.txt" | |
812 | /// "./foo.html" | |
813 | /// "./hello_world.rs" | |
814 | /// ``` | |
815 | /// | |
816 | /// The exact text, of course, depends on what files you have in `.`. | |
c34b1796 | 817 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f | 818 | pub fn path(&self) -> PathBuf { self.0.path() } |
d9579d0f AL |
819 | |
820 | /// Return the metadata for the file that this entry points at. | |
821 | /// | |
822 | /// This function will not traverse symlinks if this entry points at a | |
823 | /// symlink. | |
824 | /// | |
9cc50fc6 | 825 | /// # Platform-specific behavior |
d9579d0f AL |
826 | /// |
827 | /// On Windows this function is cheap to call (no extra system calls | |
828 | /// needed), but on Unix platforms this function is the equivalent of | |
829 | /// calling `symlink_metadata` on the path. | |
830 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
831 | pub fn metadata(&self) -> io::Result<Metadata> { | |
832 | self.0.metadata().map(Metadata) | |
833 | } | |
834 | ||
835 | /// Return the file type for the file that this entry points at. | |
836 | /// | |
837 | /// This function will not traverse symlinks if this entry points at a | |
838 | /// symlink. | |
839 | /// | |
9cc50fc6 | 840 | /// # Platform-specific behavior |
d9579d0f AL |
841 | /// |
842 | /// On Windows and most Unix platforms this function is free (no extra | |
843 | /// system calls needed), but some Unix platforms may require the equivalent | |
844 | /// call to `symlink_metadata` to learn about the target file type. | |
845 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
846 | pub fn file_type(&self) -> io::Result<FileType> { | |
847 | self.0.file_type().map(FileType) | |
848 | } | |
849 | ||
850 | /// Returns the bare file name of this directory entry without any other | |
851 | /// leading path component. | |
852 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
853 | pub fn file_name(&self) -> OsString { | |
854 | self.0.file_name() | |
855 | } | |
856 | } | |
857 | ||
858 | impl AsInner<fs_imp::DirEntry> for DirEntry { | |
859 | fn as_inner(&self) -> &fs_imp::DirEntry { &self.0 } | |
85aaf69f SL |
860 | } |
861 | ||
e9174d1e | 862 | /// Removes a file from the filesystem. |
85aaf69f | 863 | /// |
e9174d1e SL |
864 | /// Note that there is no |
865 | /// guarantee that the file is immediately deleted (e.g. depending on | |
85aaf69f SL |
866 | /// platform, other open file descriptors may prevent immediate removal). |
867 | /// | |
9cc50fc6 SL |
868 | /// # Platform-specific behavior |
869 | /// | |
870 | /// This function currently corresponds to the `unlink` function on Unix | |
871 | /// and the `DeleteFile` function on Windows. | |
872 | /// Note that, this [may change in the future][changes]. | |
873 | /// [changes]: ../io/index.html#platform-specific-behavior | |
874 | /// | |
85aaf69f SL |
875 | /// # Errors |
876 | /// | |
9cc50fc6 SL |
877 | /// This function will return an error in the following situations, but is not |
878 | /// limited to just these cases: | |
879 | /// | |
880 | /// * `path` points to a directory. | |
881 | /// * The user lacks permissions to remove the file. | |
9346a6ac AL |
882 | /// |
883 | /// # Examples | |
884 | /// | |
885 | /// ``` | |
886 | /// use std::fs; | |
887 | /// | |
888 | /// # fn foo() -> std::io::Result<()> { | |
889 | /// try!(fs::remove_file("a.txt")); | |
890 | /// # Ok(()) | |
891 | /// # } | |
892 | /// ``` | |
c34b1796 AL |
893 | #[stable(feature = "rust1", since = "1.0.0")] |
894 | pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
895 | fs_imp::unlink(path.as_ref()) | |
85aaf69f SL |
896 | } |
897 | ||
898 | /// Given a path, query the file system to get information about a file, | |
899 | /// directory, etc. | |
900 | /// | |
d9579d0f | 901 | /// This function will traverse symbolic links to query information about the |
85aaf69f SL |
902 | /// destination file. |
903 | /// | |
9cc50fc6 SL |
904 | /// # Platform-specific behavior |
905 | /// | |
906 | /// This function currently corresponds to the `stat` function on Unix | |
907 | /// and the `GetFileAttributesEx` function on Windows. | |
908 | /// Note that, this [may change in the future][changes]. | |
909 | /// [changes]: ../io/index.html#platform-specific-behavior | |
910 | /// | |
911 | /// # Errors | |
912 | /// | |
913 | /// This function will return an error in the following situations, but is not | |
914 | /// limited to just these cases: | |
915 | /// | |
916 | /// * The user lacks permissions to perform `metadata` call on `path`. | |
917 | /// * `path` does not exist. | |
918 | /// | |
c34b1796 | 919 | /// # Examples |
85aaf69f | 920 | /// |
9346a6ac | 921 | /// ```rust |
85aaf69f SL |
922 | /// # fn foo() -> std::io::Result<()> { |
923 | /// use std::fs; | |
924 | /// | |
925 | /// let attr = try!(fs::metadata("/some/file/path.txt")); | |
926 | /// // inspect attr ... | |
927 | /// # Ok(()) | |
928 | /// # } | |
929 | /// ``` | |
c34b1796 AL |
930 | #[stable(feature = "rust1", since = "1.0.0")] |
931 | pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { | |
932 | fs_imp::stat(path.as_ref()).map(Metadata) | |
85aaf69f SL |
933 | } |
934 | ||
d9579d0f AL |
935 | /// Query the metadata about a file without following symlinks. |
936 | /// | |
9cc50fc6 SL |
937 | /// # Platform-specific behavior |
938 | /// | |
939 | /// This function currently corresponds to the `lstat` function on Unix | |
940 | /// and the `GetFileAttributesEx` function on Windows. | |
941 | /// Note that, this [may change in the future][changes]. | |
942 | /// [changes]: ../io/index.html#platform-specific-behavior | |
943 | /// | |
944 | /// # Errors | |
945 | /// | |
946 | /// This function will return an error in the following situations, but is not | |
947 | /// limited to just these cases: | |
948 | /// | |
949 | /// * The user lacks permissions to perform `metadata` call on `path`. | |
950 | /// * `path` does not exist. | |
951 | /// | |
d9579d0f AL |
952 | /// # Examples |
953 | /// | |
954 | /// ```rust | |
955 | /// # fn foo() -> std::io::Result<()> { | |
956 | /// use std::fs; | |
957 | /// | |
958 | /// let attr = try!(fs::symlink_metadata("/some/file/path.txt")); | |
959 | /// // inspect attr ... | |
960 | /// # Ok(()) | |
961 | /// # } | |
962 | /// ``` | |
963 | #[stable(feature = "symlink_metadata", since = "1.1.0")] | |
964 | pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { | |
965 | fs_imp::lstat(path.as_ref()).map(Metadata) | |
966 | } | |
967 | ||
85aaf69f SL |
968 | /// Rename a file or directory to a new name. |
969 | /// | |
62682a34 SL |
970 | /// This will not work if the new name is on a different mount point. |
971 | /// | |
9cc50fc6 SL |
972 | /// # Platform-specific behavior |
973 | /// | |
974 | /// This function currently corresponds to the `rename` function on Unix | |
975 | /// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. | |
976 | /// Note that, this [may change in the future][changes]. | |
977 | /// [changes]: ../io/index.html#platform-specific-behavior | |
978 | /// | |
85aaf69f SL |
979 | /// # Errors |
980 | /// | |
9cc50fc6 SL |
981 | /// This function will return an error in the following situations, but is not |
982 | /// limited to just these cases: | |
983 | /// | |
984 | /// * `from` does not exist. | |
985 | /// * The user lacks permissions to view contents. | |
986 | /// * `from` and `to` are on separate filesystems. | |
9346a6ac AL |
987 | /// |
988 | /// # Examples | |
989 | /// | |
990 | /// ``` | |
991 | /// use std::fs; | |
992 | /// | |
993 | /// # fn foo() -> std::io::Result<()> { | |
9cc50fc6 | 994 | /// try!(fs::rename("a.txt", "b.txt")); // Rename a.txt to b.txt |
9346a6ac AL |
995 | /// # Ok(()) |
996 | /// # } | |
997 | /// ``` | |
c34b1796 AL |
998 | #[stable(feature = "rust1", since = "1.0.0")] |
999 | pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { | |
1000 | fs_imp::rename(from.as_ref(), to.as_ref()) | |
85aaf69f SL |
1001 | } |
1002 | ||
1003 | /// Copies the contents of one file to another. This function will also | |
1004 | /// copy the permission bits of the original file to the destination file. | |
1005 | /// | |
1006 | /// This function will **overwrite** the contents of `to`. | |
1007 | /// | |
1008 | /// Note that if `from` and `to` both point to the same file, then the file | |
1009 | /// will likely get truncated by this operation. | |
1010 | /// | |
e9174d1e SL |
1011 | /// On success, the total number of bytes copied is returned. |
1012 | /// | |
9cc50fc6 SL |
1013 | /// # Platform-specific behavior |
1014 | /// | |
1015 | /// This function currently corresponds to the `open` function in Unix | |
1016 | /// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. | |
1017 | /// `O_CLOEXEC` is set for returned file descriptors. | |
1018 | /// On Windows, this function currently corresponds to `CopyFileEx`. | |
1019 | /// Note that, this [may change in the future][changes]. | |
1020 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1021 | /// | |
85aaf69f SL |
1022 | /// # Errors |
1023 | /// | |
1024 | /// This function will return an error in the following situations, but is not | |
1025 | /// limited to just these cases: | |
1026 | /// | |
9cc50fc6 SL |
1027 | /// * The `from` path is not a file. |
1028 | /// * The `from` file does not exist. | |
85aaf69f | 1029 | /// * The current process does not have the permission rights to access |
9cc50fc6 | 1030 | /// `from` or write `to`. |
9346a6ac AL |
1031 | /// |
1032 | /// # Examples | |
1033 | /// | |
1034 | /// ```no_run | |
1035 | /// use std::fs; | |
1036 | /// | |
1037 | /// # fn foo() -> std::io::Result<()> { | |
9cc50fc6 | 1038 | /// try!(fs::copy("foo.txt", "bar.txt")); // Copy foo.txt to bar.txt |
9346a6ac AL |
1039 | /// # Ok(()) } |
1040 | /// ``` | |
c34b1796 AL |
1041 | #[stable(feature = "rust1", since = "1.0.0")] |
1042 | pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { | |
c1a9b12d | 1043 | fs_imp::copy(from.as_ref(), to.as_ref()) |
85aaf69f SL |
1044 | } |
1045 | ||
1046 | /// Creates a new hard link on the filesystem. | |
1047 | /// | |
1048 | /// The `dst` path will be a link pointing to the `src` path. Note that systems | |
1049 | /// often require these two paths to both be located on the same filesystem. | |
9346a6ac | 1050 | /// |
9cc50fc6 SL |
1051 | /// # Platform-specific behavior |
1052 | /// | |
1053 | /// This function currently corresponds to the `link` function on Unix | |
1054 | /// and the `CreateHardLink` function on Windows. | |
1055 | /// Note that, this [may change in the future][changes]. | |
1056 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1057 | /// | |
1058 | /// # Errors | |
1059 | /// | |
1060 | /// This function will return an error in the following situations, but is not | |
1061 | /// limited to just these cases: | |
1062 | /// | |
1063 | /// * The `src` path is not a file or doesn't exist. | |
1064 | /// | |
9346a6ac AL |
1065 | /// # Examples |
1066 | /// | |
1067 | /// ``` | |
1068 | /// use std::fs; | |
1069 | /// | |
1070 | /// # fn foo() -> std::io::Result<()> { | |
9cc50fc6 | 1071 | /// try!(fs::hard_link("a.txt", "b.txt")); // Hard link a.txt to b.txt |
9346a6ac AL |
1072 | /// # Ok(()) |
1073 | /// # } | |
1074 | /// ``` | |
c34b1796 AL |
1075 | #[stable(feature = "rust1", since = "1.0.0")] |
1076 | pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { | |
1077 | fs_imp::link(src.as_ref(), dst.as_ref()) | |
85aaf69f SL |
1078 | } |
1079 | ||
d9579d0f | 1080 | /// Creates a new symbolic link on the filesystem. |
85aaf69f | 1081 | /// |
d9579d0f AL |
1082 | /// The `dst` path will be a symbolic link pointing to the `src` path. |
1083 | /// On Windows, this will be a file symlink, not a directory symlink; | |
1084 | /// for this reason, the platform-specific `std::os::unix::fs::symlink` | |
1085 | /// and `std::os::windows::fs::{symlink_file, symlink_dir}` should be | |
1086 | /// used instead to make the intent explicit. | |
9346a6ac AL |
1087 | /// |
1088 | /// # Examples | |
1089 | /// | |
1090 | /// ``` | |
1091 | /// use std::fs; | |
1092 | /// | |
1093 | /// # fn foo() -> std::io::Result<()> { | |
1094 | /// try!(fs::soft_link("a.txt", "b.txt")); | |
1095 | /// # Ok(()) | |
1096 | /// # } | |
1097 | /// ``` | |
9cc50fc6 | 1098 | #[stable(feature = "rust1", since = "1.0.0")] |
92a42be0 | 1099 | #[rustc_deprecated(since = "1.1.0", |
d9579d0f AL |
1100 | reason = "replaced with std::os::unix::fs::symlink and \ |
1101 | std::os::windows::fs::{symlink_file, symlink_dir}")] | |
c34b1796 AL |
1102 | pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
1103 | fs_imp::symlink(src.as_ref(), dst.as_ref()) | |
85aaf69f SL |
1104 | } |
1105 | ||
d9579d0f | 1106 | /// Reads a symbolic link, returning the file that the link points to. |
85aaf69f | 1107 | /// |
9cc50fc6 SL |
1108 | /// # Platform-specific behavior |
1109 | /// | |
1110 | /// This function currently corresponds to the `readlink` function on Unix | |
1111 | /// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and | |
1112 | /// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. | |
1113 | /// Note that, this [may change in the future][changes]. | |
1114 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1115 | /// | |
85aaf69f SL |
1116 | /// # Errors |
1117 | /// | |
9cc50fc6 SL |
1118 | /// This function will return an error in the following situations, but is not |
1119 | /// limited to just these cases: | |
1120 | /// | |
1121 | /// * `path` is not a symbolic link. | |
1122 | /// * `path` does not exist. | |
85aaf69f | 1123 | /// |
c34b1796 | 1124 | /// # Examples |
85aaf69f | 1125 | /// |
c34b1796 | 1126 | /// ``` |
85aaf69f SL |
1127 | /// use std::fs; |
1128 | /// | |
9346a6ac AL |
1129 | /// # fn foo() -> std::io::Result<()> { |
1130 | /// let path = try!(fs::read_link("a.txt")); | |
1131 | /// # Ok(()) | |
1132 | /// # } | |
85aaf69f | 1133 | /// ``` |
9346a6ac AL |
1134 | #[stable(feature = "rust1", since = "1.0.0")] |
1135 | pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { | |
1136 | fs_imp::readlink(path.as_ref()) | |
1137 | } | |
1138 | ||
d9579d0f AL |
1139 | /// Returns the canonical form of a path with all intermediate components |
1140 | /// normalized and symbolic links resolved. | |
b039eaaf | 1141 | /// |
9cc50fc6 SL |
1142 | /// # Platform-specific behavior |
1143 | /// | |
1144 | /// This function currently corresponds to the `realpath` function on Unix | |
1145 | /// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. | |
1146 | /// Note that, this [may change in the future][changes]. | |
1147 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1148 | /// | |
1149 | /// # Errors | |
1150 | /// | |
1151 | /// This function will return an error in the following situations, but is not | |
1152 | /// limited to just these cases: | |
1153 | /// | |
1154 | /// * `path` does not exist. | |
1155 | /// * A component in path is not a directory. | |
b039eaaf SL |
1156 | /// |
1157 | /// # Examples | |
1158 | /// | |
1159 | /// ``` | |
1160 | /// use std::fs; | |
1161 | /// | |
1162 | /// # fn foo() -> std::io::Result<()> { | |
1163 | /// let path = try!(fs::canonicalize("../a/../foo.txt")); | |
1164 | /// # Ok(()) | |
1165 | /// # } | |
1166 | /// ``` | |
1167 | #[stable(feature = "fs_canonicalize", since = "1.5.0")] | |
d9579d0f AL |
1168 | pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { |
1169 | fs_imp::canonicalize(path.as_ref()) | |
1170 | } | |
1171 | ||
9346a6ac | 1172 | /// Creates a new, empty directory at the provided path |
85aaf69f | 1173 | /// |
9cc50fc6 SL |
1174 | /// # Platform-specific behavior |
1175 | /// | |
1176 | /// This function currently corresponds to the `mkdir` function on Unix | |
1177 | /// and the `CreateDirectory` function on Windows. | |
1178 | /// Note that, this [may change in the future][changes]. | |
1179 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1180 | /// | |
85aaf69f SL |
1181 | /// # Errors |
1182 | /// | |
9cc50fc6 SL |
1183 | /// This function will return an error in the following situations, but is not |
1184 | /// limited to just these cases: | |
1185 | /// | |
1186 | /// * User lacks permissions to create directory at `path`. | |
1187 | /// * `path` already exists. | |
9346a6ac AL |
1188 | /// |
1189 | /// # Examples | |
1190 | /// | |
1191 | /// ``` | |
1192 | /// use std::fs; | |
1193 | /// | |
1194 | /// # fn foo() -> std::io::Result<()> { | |
1195 | /// try!(fs::create_dir("/some/dir")); | |
1196 | /// # Ok(()) | |
1197 | /// # } | |
1198 | /// ``` | |
c34b1796 AL |
1199 | #[stable(feature = "rust1", since = "1.0.0")] |
1200 | pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
d9579d0f | 1201 | DirBuilder::new().create(path.as_ref()) |
85aaf69f SL |
1202 | } |
1203 | ||
1204 | /// Recursively create a directory and all of its parent components if they | |
1205 | /// are missing. | |
1206 | /// | |
9cc50fc6 SL |
1207 | /// # Platform-specific behavior |
1208 | /// | |
1209 | /// This function currently corresponds to the `mkdir` function on Unix | |
1210 | /// and the `CreateDirectory` function on Windows. | |
1211 | /// Note that, this [may change in the future][changes]. | |
1212 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1213 | /// | |
85aaf69f SL |
1214 | /// # Errors |
1215 | /// | |
9cc50fc6 SL |
1216 | /// This function will return an error in the following situations, but is not |
1217 | /// limited to just these cases: | |
1218 | /// | |
1219 | /// * If any directory in the path specified by `path` | |
85aaf69f SL |
1220 | /// does not already exist and it could not be created otherwise. The specific |
1221 | /// error conditions for when a directory is being created (after it is | |
1222 | /// determined to not exist) are outlined by `fs::create_dir`. | |
9346a6ac AL |
1223 | /// |
1224 | /// # Examples | |
1225 | /// | |
1226 | /// ``` | |
1227 | /// use std::fs; | |
1228 | /// | |
1229 | /// # fn foo() -> std::io::Result<()> { | |
1230 | /// try!(fs::create_dir_all("/some/dir")); | |
1231 | /// # Ok(()) | |
1232 | /// # } | |
1233 | /// ``` | |
c34b1796 AL |
1234 | #[stable(feature = "rust1", since = "1.0.0")] |
1235 | pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
d9579d0f | 1236 | DirBuilder::new().recursive(true).create(path.as_ref()) |
85aaf69f SL |
1237 | } |
1238 | ||
9346a6ac AL |
1239 | /// Removes an existing, empty directory. |
1240 | /// | |
9cc50fc6 SL |
1241 | /// # Platform-specific behavior |
1242 | /// | |
1243 | /// This function currently corresponds to the `rmdir` function on Unix | |
1244 | /// and the `RemoveDirectory` function on Windows. | |
1245 | /// Note that, this [may change in the future][changes]. | |
1246 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1247 | /// | |
9346a6ac AL |
1248 | /// # Errors |
1249 | /// | |
9cc50fc6 SL |
1250 | /// This function will return an error in the following situations, but is not |
1251 | /// limited to just these cases: | |
1252 | /// | |
1253 | /// * The user lacks permissions to remove the directory at the provided `path`. | |
1254 | /// * The directory isn't empty. | |
85aaf69f | 1255 | /// |
c34b1796 | 1256 | /// # Examples |
85aaf69f | 1257 | /// |
c34b1796 | 1258 | /// ``` |
85aaf69f SL |
1259 | /// use std::fs; |
1260 | /// | |
9346a6ac AL |
1261 | /// # fn foo() -> std::io::Result<()> { |
1262 | /// try!(fs::remove_dir("/some/dir")); | |
1263 | /// # Ok(()) | |
1264 | /// # } | |
85aaf69f | 1265 | /// ``` |
c34b1796 AL |
1266 | #[stable(feature = "rust1", since = "1.0.0")] |
1267 | pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
1268 | fs_imp::rmdir(path.as_ref()) | |
85aaf69f SL |
1269 | } |
1270 | ||
1271 | /// Removes a directory at this path, after removing all its contents. Use | |
1272 | /// carefully! | |
1273 | /// | |
d9579d0f AL |
1274 | /// This function does **not** follow symbolic links and it will simply remove the |
1275 | /// symbolic link itself. | |
85aaf69f | 1276 | /// |
9cc50fc6 SL |
1277 | /// # Platform-specific behavior |
1278 | /// | |
1279 | /// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix | |
1280 | /// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions | |
1281 | /// on Windows. | |
1282 | /// Note that, this [may change in the future][changes]. | |
1283 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1284 | /// | |
85aaf69f SL |
1285 | /// # Errors |
1286 | /// | |
9346a6ac AL |
1287 | /// See `file::remove_file` and `fs::remove_dir`. |
1288 | /// | |
1289 | /// # Examples | |
1290 | /// | |
1291 | /// ``` | |
1292 | /// use std::fs; | |
1293 | /// | |
1294 | /// # fn foo() -> std::io::Result<()> { | |
1295 | /// try!(fs::remove_dir_all("/some/dir")); | |
1296 | /// # Ok(()) | |
1297 | /// # } | |
1298 | /// ``` | |
c34b1796 AL |
1299 | #[stable(feature = "rust1", since = "1.0.0")] |
1300 | pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
7453a54e | 1301 | fs_imp::remove_dir_all(path.as_ref()) |
85aaf69f SL |
1302 | } |
1303 | ||
1304 | /// Returns an iterator over the entries within a directory. | |
1305 | /// | |
1306 | /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may | |
1307 | /// be encountered after an iterator is initially constructed. | |
1308 | /// | |
9cc50fc6 SL |
1309 | /// # Platform-specific behavior |
1310 | /// | |
1311 | /// This function currently corresponds to the `opendir` function on Unix | |
1312 | /// and the `FindFirstFile` function on Windows. | |
1313 | /// Note that, this [may change in the future][changes]. | |
1314 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1315 | /// | |
1316 | /// # Errors | |
1317 | /// | |
1318 | /// This function will return an error in the following situations, but is not | |
1319 | /// limited to just these cases: | |
1320 | /// | |
1321 | /// * The provided `path` doesn't exist. | |
1322 | /// * The process lacks permissions to view the contents. | |
1323 | /// * The `path` points at a non-directory file. | |
1324 | /// | |
c34b1796 | 1325 | /// # Examples |
85aaf69f | 1326 | /// |
c34b1796 | 1327 | /// ``` |
85aaf69f | 1328 | /// use std::io; |
62682a34 | 1329 | /// use std::fs::{self, DirEntry}; |
85aaf69f SL |
1330 | /// use std::path::Path; |
1331 | /// | |
54a0048b | 1332 | /// // one possible implementation of walking a directory only visiting files |
62682a34 SL |
1333 | /// fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> { |
1334 | /// if try!(fs::metadata(dir)).is_dir() { | |
85aaf69f SL |
1335 | /// for entry in try!(fs::read_dir(dir)) { |
1336 | /// let entry = try!(entry); | |
62682a34 | 1337 | /// if try!(fs::metadata(entry.path())).is_dir() { |
85aaf69f SL |
1338 | /// try!(visit_dirs(&entry.path(), cb)); |
1339 | /// } else { | |
62682a34 | 1340 | /// cb(&entry); |
85aaf69f SL |
1341 | /// } |
1342 | /// } | |
1343 | /// } | |
1344 | /// Ok(()) | |
1345 | /// } | |
1346 | /// ``` | |
c34b1796 AL |
1347 | #[stable(feature = "rust1", since = "1.0.0")] |
1348 | pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { | |
1349 | fs_imp::readdir(path.as_ref()).map(ReadDir) | |
85aaf69f SL |
1350 | } |
1351 | ||
85aaf69f SL |
1352 | /// Changes the permissions found on a file or a directory. |
1353 | /// | |
9cc50fc6 SL |
1354 | /// # Platform-specific behavior |
1355 | /// | |
1356 | /// This function currently corresponds to the `chmod` function on Unix | |
1357 | /// and the `SetFileAttributes` function on Windows. | |
1358 | /// Note that, this [may change in the future][changes]. | |
1359 | /// [changes]: ../io/index.html#platform-specific-behavior | |
1360 | /// | |
1361 | /// # Errors | |
1362 | /// | |
1363 | /// This function will return an error in the following situations, but is not | |
1364 | /// limited to just these cases: | |
1365 | /// | |
1366 | /// * `path` does not exist. | |
1367 | /// * The user lacks the permission to change attributes of the file. | |
1368 | /// | |
c34b1796 | 1369 | /// # Examples |
85aaf69f SL |
1370 | /// |
1371 | /// ``` | |
1372 | /// # fn foo() -> std::io::Result<()> { | |
1373 | /// use std::fs; | |
1374 | /// | |
1375 | /// let mut perms = try!(fs::metadata("foo.txt")).permissions(); | |
1376 | /// perms.set_readonly(true); | |
1377 | /// try!(fs::set_permissions("foo.txt", perms)); | |
1378 | /// # Ok(()) | |
1379 | /// # } | |
1380 | /// ``` | |
d9579d0f AL |
1381 | #[stable(feature = "set_permissions", since = "1.1.0")] |
1382 | pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) | |
1383 | -> io::Result<()> { | |
c34b1796 | 1384 | fs_imp::set_perm(path.as_ref(), perm.0) |
85aaf69f SL |
1385 | } |
1386 | ||
d9579d0f AL |
1387 | impl DirBuilder { |
1388 | /// Creates a new set of options with default mode/security settings for all | |
1389 | /// platforms and also non-recursive. | |
92a42be0 | 1390 | #[stable(feature = "dir_builder", since = "1.6.0")] |
d9579d0f AL |
1391 | pub fn new() -> DirBuilder { |
1392 | DirBuilder { | |
1393 | inner: fs_imp::DirBuilder::new(), | |
1394 | recursive: false, | |
1395 | } | |
1396 | } | |
1397 | ||
1398 | /// Indicate that directories create should be created recursively, creating | |
1399 | /// all parent directories if they do not exist with the same security and | |
1400 | /// permissions settings. | |
1401 | /// | |
1402 | /// This option defaults to `false` | |
92a42be0 | 1403 | #[stable(feature = "dir_builder", since = "1.6.0")] |
d9579d0f AL |
1404 | pub fn recursive(&mut self, recursive: bool) -> &mut Self { |
1405 | self.recursive = recursive; | |
1406 | self | |
1407 | } | |
1408 | ||
1409 | /// Create the specified directory with the options configured in this | |
1410 | /// builder. | |
92a42be0 SL |
1411 | /// |
1412 | /// # Examples | |
1413 | /// | |
1414 | /// ```no_run | |
1415 | /// use std::fs::{self, DirBuilder}; | |
1416 | /// | |
1417 | /// let path = "/tmp/foo/bar/baz"; | |
1418 | /// DirBuilder::new() | |
1419 | /// .recursive(true) | |
1420 | /// .create(path).unwrap(); | |
1421 | /// | |
1422 | /// assert!(fs::metadata(path).unwrap().is_dir()); | |
1423 | /// ``` | |
1424 | #[stable(feature = "dir_builder", since = "1.6.0")] | |
d9579d0f | 1425 | pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { |
e9174d1e SL |
1426 | self._create(path.as_ref()) |
1427 | } | |
1428 | ||
1429 | fn _create(&self, path: &Path) -> io::Result<()> { | |
d9579d0f AL |
1430 | if self.recursive { |
1431 | self.create_dir_all(path) | |
1432 | } else { | |
1433 | self.inner.mkdir(path) | |
1434 | } | |
1435 | } | |
1436 | ||
1437 | fn create_dir_all(&self, path: &Path) -> io::Result<()> { | |
1438 | if path == Path::new("") || path.is_dir() { return Ok(()) } | |
1439 | if let Some(p) = path.parent() { | |
54a0048b | 1440 | self.create_dir_all(p)? |
d9579d0f AL |
1441 | } |
1442 | self.inner.mkdir(path) | |
1443 | } | |
1444 | } | |
1445 | ||
1446 | impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder { | |
1447 | fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { | |
1448 | &mut self.inner | |
1449 | } | |
1450 | } | |
1451 | ||
85aaf69f SL |
1452 | #[cfg(test)] |
1453 | mod tests { | |
1454 | use prelude::v1::*; | |
1455 | use io::prelude::*; | |
1456 | ||
1457 | use fs::{self, File, OpenOptions}; | |
1458 | use io::{ErrorKind, SeekFrom}; | |
54a0048b SL |
1459 | use path::Path; |
1460 | use rand::{StdRng, Rng}; | |
85aaf69f | 1461 | use str; |
54a0048b | 1462 | use sys_common::io::test::{TempDir, tmpdir}; |
85aaf69f | 1463 | |
7453a54e SL |
1464 | #[cfg(windows)] |
1465 | use os::windows::fs::{symlink_dir, symlink_file}; | |
1466 | #[cfg(windows)] | |
1467 | use sys::fs::symlink_junction; | |
1468 | #[cfg(unix)] | |
1469 | use os::unix::fs::symlink as symlink_dir; | |
1470 | #[cfg(unix)] | |
1471 | use os::unix::fs::symlink as symlink_file; | |
1472 | #[cfg(unix)] | |
1473 | use os::unix::fs::symlink as symlink_junction; | |
1474 | ||
85aaf69f SL |
1475 | macro_rules! check { ($e:expr) => ( |
1476 | match $e { | |
1477 | Ok(t) => t, | |
1478 | Err(e) => panic!("{} failed with: {}", stringify!($e), e), | |
1479 | } | |
1480 | ) } | |
1481 | ||
1482 | macro_rules! error { ($e:expr, $s:expr) => ( | |
1483 | match $e { | |
1484 | Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), | |
c34b1796 | 1485 | Err(ref err) => assert!(err.to_string().contains($s), |
85aaf69f SL |
1486 | format!("`{}` did not contain `{}`", err, $s)) |
1487 | } | |
1488 | ) } | |
1489 | ||
7453a54e SL |
1490 | // Several test fail on windows if the user does not have permission to |
1491 | // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of | |
1492 | // disabling these test on Windows, use this function to test whether we | |
1493 | // have permission, and return otherwise. This way, we still don't run these | |
1494 | // tests most of the time, but at least we do if the user has the right | |
1495 | // permissions. | |
1496 | pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { | |
1497 | if cfg!(unix) { return true } | |
1498 | let link = tmpdir.join("some_hopefully_unique_link_name"); | |
1499 | ||
1500 | match symlink_file(r"nonexisting_target", link) { | |
1501 | Ok(_) => true, | |
1502 | Err(ref err) => | |
1503 | if err.to_string().contains("A required privilege is not held by the client.") { | |
1504 | false | |
1505 | } else { | |
1506 | true | |
1507 | } | |
1508 | } | |
1509 | } | |
1510 | ||
85aaf69f SL |
1511 | #[test] |
1512 | fn file_test_io_smoke_test() { | |
1513 | let message = "it's alright. have a good time"; | |
1514 | let tmpdir = tmpdir(); | |
1515 | let filename = &tmpdir.join("file_rt_io_file_test.txt"); | |
1516 | { | |
1517 | let mut write_stream = check!(File::create(filename)); | |
1518 | check!(write_stream.write(message.as_bytes())); | |
1519 | } | |
1520 | { | |
1521 | let mut read_stream = check!(File::open(filename)); | |
1522 | let mut read_buf = [0; 1028]; | |
1523 | let read_str = match check!(read_stream.read(&mut read_buf)) { | |
c34b1796 | 1524 | 0 => panic!("shouldn't happen"), |
85aaf69f SL |
1525 | n => str::from_utf8(&read_buf[..n]).unwrap().to_string() |
1526 | }; | |
c34b1796 | 1527 | assert_eq!(read_str, message); |
85aaf69f SL |
1528 | } |
1529 | check!(fs::remove_file(filename)); | |
1530 | } | |
1531 | ||
1532 | #[test] | |
1533 | fn invalid_path_raises() { | |
1534 | let tmpdir = tmpdir(); | |
1535 | let filename = &tmpdir.join("file_that_does_not_exist.txt"); | |
1536 | let result = File::open(filename); | |
1537 | ||
1538 | if cfg!(unix) { | |
1539 | error!(result, "o such file or directory"); | |
1540 | } | |
7453a54e SL |
1541 | if cfg!(windows) { |
1542 | error!(result, "The system cannot find the file specified"); | |
1543 | } | |
85aaf69f SL |
1544 | } |
1545 | ||
1546 | #[test] | |
1547 | fn file_test_iounlinking_invalid_path_should_raise_condition() { | |
1548 | let tmpdir = tmpdir(); | |
1549 | let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); | |
1550 | ||
1551 | let result = fs::remove_file(filename); | |
1552 | ||
1553 | if cfg!(unix) { | |
1554 | error!(result, "o such file or directory"); | |
1555 | } | |
7453a54e SL |
1556 | if cfg!(windows) { |
1557 | error!(result, "The system cannot find the file specified"); | |
1558 | } | |
85aaf69f SL |
1559 | } |
1560 | ||
1561 | #[test] | |
1562 | fn file_test_io_non_positional_read() { | |
1563 | let message: &str = "ten-four"; | |
1564 | let mut read_mem = [0; 8]; | |
1565 | let tmpdir = tmpdir(); | |
1566 | let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); | |
1567 | { | |
1568 | let mut rw_stream = check!(File::create(filename)); | |
1569 | check!(rw_stream.write(message.as_bytes())); | |
1570 | } | |
1571 | { | |
1572 | let mut read_stream = check!(File::open(filename)); | |
1573 | { | |
1574 | let read_buf = &mut read_mem[0..4]; | |
1575 | check!(read_stream.read(read_buf)); | |
1576 | } | |
1577 | { | |
1578 | let read_buf = &mut read_mem[4..8]; | |
1579 | check!(read_stream.read(read_buf)); | |
1580 | } | |
1581 | } | |
1582 | check!(fs::remove_file(filename)); | |
1583 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1584 | assert_eq!(read_str, message); | |
1585 | } | |
1586 | ||
1587 | #[test] | |
1588 | fn file_test_io_seek_and_tell_smoke_test() { | |
1589 | let message = "ten-four"; | |
1590 | let mut read_mem = [0; 4]; | |
1591 | let set_cursor = 4 as u64; | |
9cc50fc6 SL |
1592 | let tell_pos_pre_read; |
1593 | let tell_pos_post_read; | |
85aaf69f SL |
1594 | let tmpdir = tmpdir(); |
1595 | let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); | |
1596 | { | |
1597 | let mut rw_stream = check!(File::create(filename)); | |
1598 | check!(rw_stream.write(message.as_bytes())); | |
1599 | } | |
1600 | { | |
1601 | let mut read_stream = check!(File::open(filename)); | |
1602 | check!(read_stream.seek(SeekFrom::Start(set_cursor))); | |
1603 | tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
1604 | check!(read_stream.read(&mut read_mem)); | |
1605 | tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
1606 | } | |
1607 | check!(fs::remove_file(filename)); | |
1608 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1609 | assert_eq!(read_str, &message[4..8]); | |
1610 | assert_eq!(tell_pos_pre_read, set_cursor); | |
1611 | assert_eq!(tell_pos_post_read, message.len() as u64); | |
1612 | } | |
1613 | ||
1614 | #[test] | |
1615 | fn file_test_io_seek_and_write() { | |
1616 | let initial_msg = "food-is-yummy"; | |
1617 | let overwrite_msg = "-the-bar!!"; | |
1618 | let final_msg = "foo-the-bar!!"; | |
1619 | let seek_idx = 3; | |
1620 | let mut read_mem = [0; 13]; | |
1621 | let tmpdir = tmpdir(); | |
1622 | let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); | |
1623 | { | |
1624 | let mut rw_stream = check!(File::create(filename)); | |
1625 | check!(rw_stream.write(initial_msg.as_bytes())); | |
1626 | check!(rw_stream.seek(SeekFrom::Start(seek_idx))); | |
1627 | check!(rw_stream.write(overwrite_msg.as_bytes())); | |
1628 | } | |
1629 | { | |
1630 | let mut read_stream = check!(File::open(filename)); | |
1631 | check!(read_stream.read(&mut read_mem)); | |
1632 | } | |
1633 | check!(fs::remove_file(filename)); | |
1634 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1635 | assert!(read_str == final_msg); | |
1636 | } | |
1637 | ||
1638 | #[test] | |
1639 | fn file_test_io_seek_shakedown() { | |
1640 | // 01234567890123 | |
1641 | let initial_msg = "qwer-asdf-zxcv"; | |
1642 | let chunk_one: &str = "qwer"; | |
1643 | let chunk_two: &str = "asdf"; | |
1644 | let chunk_three: &str = "zxcv"; | |
1645 | let mut read_mem = [0; 4]; | |
1646 | let tmpdir = tmpdir(); | |
1647 | let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); | |
1648 | { | |
1649 | let mut rw_stream = check!(File::create(filename)); | |
1650 | check!(rw_stream.write(initial_msg.as_bytes())); | |
1651 | } | |
1652 | { | |
1653 | let mut read_stream = check!(File::open(filename)); | |
1654 | ||
1655 | check!(read_stream.seek(SeekFrom::End(-4))); | |
1656 | check!(read_stream.read(&mut read_mem)); | |
1657 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); | |
1658 | ||
1659 | check!(read_stream.seek(SeekFrom::Current(-9))); | |
1660 | check!(read_stream.read(&mut read_mem)); | |
1661 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); | |
1662 | ||
1663 | check!(read_stream.seek(SeekFrom::Start(0))); | |
1664 | check!(read_stream.read(&mut read_mem)); | |
1665 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); | |
1666 | } | |
1667 | check!(fs::remove_file(filename)); | |
1668 | } | |
1669 | ||
1670 | #[test] | |
1671 | fn file_test_stat_is_correct_on_is_file() { | |
1672 | let tmpdir = tmpdir(); | |
1673 | let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); | |
1674 | { | |
1675 | let mut opts = OpenOptions::new(); | |
1676 | let mut fs = check!(opts.read(true).write(true) | |
1677 | .create(true).open(filename)); | |
1678 | let msg = "hw"; | |
1679 | fs.write(msg.as_bytes()).unwrap(); | |
1680 | ||
1681 | let fstat_res = check!(fs.metadata()); | |
1682 | assert!(fstat_res.is_file()); | |
1683 | } | |
1684 | let stat_res_fn = check!(fs::metadata(filename)); | |
1685 | assert!(stat_res_fn.is_file()); | |
1686 | let stat_res_meth = check!(filename.metadata()); | |
1687 | assert!(stat_res_meth.is_file()); | |
1688 | check!(fs::remove_file(filename)); | |
1689 | } | |
1690 | ||
1691 | #[test] | |
1692 | fn file_test_stat_is_correct_on_is_dir() { | |
1693 | let tmpdir = tmpdir(); | |
1694 | let filename = &tmpdir.join("file_stat_correct_on_is_dir"); | |
1695 | check!(fs::create_dir(filename)); | |
1696 | let stat_res_fn = check!(fs::metadata(filename)); | |
1697 | assert!(stat_res_fn.is_dir()); | |
1698 | let stat_res_meth = check!(filename.metadata()); | |
1699 | assert!(stat_res_meth.is_dir()); | |
1700 | check!(fs::remove_dir(filename)); | |
1701 | } | |
1702 | ||
1703 | #[test] | |
1704 | fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { | |
1705 | let tmpdir = tmpdir(); | |
1706 | let dir = &tmpdir.join("fileinfo_false_on_dir"); | |
1707 | check!(fs::create_dir(dir)); | |
54a0048b | 1708 | assert!(!dir.is_file()); |
85aaf69f SL |
1709 | check!(fs::remove_dir(dir)); |
1710 | } | |
1711 | ||
1712 | #[test] | |
1713 | fn file_test_fileinfo_check_exists_before_and_after_file_creation() { | |
1714 | let tmpdir = tmpdir(); | |
1715 | let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); | |
1716 | check!(check!(File::create(file)).write(b"foo")); | |
1717 | assert!(file.exists()); | |
1718 | check!(fs::remove_file(file)); | |
1719 | assert!(!file.exists()); | |
1720 | } | |
1721 | ||
1722 | #[test] | |
1723 | fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { | |
1724 | let tmpdir = tmpdir(); | |
1725 | let dir = &tmpdir.join("before_and_after_dir"); | |
1726 | assert!(!dir.exists()); | |
1727 | check!(fs::create_dir(dir)); | |
1728 | assert!(dir.exists()); | |
1729 | assert!(dir.is_dir()); | |
1730 | check!(fs::remove_dir(dir)); | |
1731 | assert!(!dir.exists()); | |
1732 | } | |
1733 | ||
1734 | #[test] | |
1735 | fn file_test_directoryinfo_readdir() { | |
1736 | let tmpdir = tmpdir(); | |
1737 | let dir = &tmpdir.join("di_readdir"); | |
1738 | check!(fs::create_dir(dir)); | |
1739 | let prefix = "foo"; | |
c34b1796 | 1740 | for n in 0..3 { |
85aaf69f SL |
1741 | let f = dir.join(&format!("{}.txt", n)); |
1742 | let mut w = check!(File::create(&f)); | |
1743 | let msg_str = format!("{}{}", prefix, n.to_string()); | |
1744 | let msg = msg_str.as_bytes(); | |
1745 | check!(w.write(msg)); | |
1746 | } | |
c34b1796 AL |
1747 | let files = check!(fs::read_dir(dir)); |
1748 | let mut mem = [0; 4]; | |
85aaf69f SL |
1749 | for f in files { |
1750 | let f = f.unwrap().path(); | |
1751 | { | |
1752 | let n = f.file_stem().unwrap(); | |
1753 | check!(check!(File::open(&f)).read(&mut mem)); | |
1754 | let read_str = str::from_utf8(&mem).unwrap(); | |
1755 | let expected = format!("{}{}", prefix, n.to_str().unwrap()); | |
c34b1796 | 1756 | assert_eq!(expected, read_str); |
85aaf69f SL |
1757 | } |
1758 | check!(fs::remove_file(&f)); | |
1759 | } | |
1760 | check!(fs::remove_dir(dir)); | |
1761 | } | |
1762 | ||
85aaf69f SL |
1763 | #[test] |
1764 | fn mkdir_path_already_exists_error() { | |
1765 | let tmpdir = tmpdir(); | |
1766 | let dir = &tmpdir.join("mkdir_error_twice"); | |
1767 | check!(fs::create_dir(dir)); | |
1768 | let e = fs::create_dir(dir).err().unwrap(); | |
c34b1796 | 1769 | assert_eq!(e.kind(), ErrorKind::AlreadyExists); |
85aaf69f SL |
1770 | } |
1771 | ||
1772 | #[test] | |
1773 | fn recursive_mkdir() { | |
1774 | let tmpdir = tmpdir(); | |
1775 | let dir = tmpdir.join("d1/d2"); | |
1776 | check!(fs::create_dir_all(&dir)); | |
1777 | assert!(dir.is_dir()) | |
1778 | } | |
1779 | ||
1780 | #[test] | |
1781 | fn recursive_mkdir_failure() { | |
1782 | let tmpdir = tmpdir(); | |
1783 | let dir = tmpdir.join("d1"); | |
1784 | let file = dir.join("f1"); | |
1785 | ||
1786 | check!(fs::create_dir_all(&dir)); | |
1787 | check!(File::create(&file)); | |
1788 | ||
1789 | let result = fs::create_dir_all(&file); | |
1790 | ||
1791 | assert!(result.is_err()); | |
85aaf69f SL |
1792 | } |
1793 | ||
1794 | #[test] | |
1795 | fn recursive_mkdir_slash() { | |
7453a54e | 1796 | check!(fs::create_dir_all(&Path::new("/"))); |
85aaf69f SL |
1797 | } |
1798 | ||
85aaf69f SL |
1799 | #[test] |
1800 | fn recursive_rmdir() { | |
1801 | let tmpdir = tmpdir(); | |
1802 | let d1 = tmpdir.join("d1"); | |
1803 | let dt = d1.join("t"); | |
1804 | let dtt = dt.join("t"); | |
1805 | let d2 = tmpdir.join("d2"); | |
1806 | let canary = d2.join("do_not_delete"); | |
1807 | check!(fs::create_dir_all(&dtt)); | |
1808 | check!(fs::create_dir_all(&d2)); | |
1809 | check!(check!(File::create(&canary)).write(b"foo")); | |
7453a54e SL |
1810 | check!(symlink_junction(&d2, &dt.join("d2"))); |
1811 | let _ = symlink_file(&canary, &d1.join("canary")); | |
85aaf69f SL |
1812 | check!(fs::remove_dir_all(&d1)); |
1813 | ||
1814 | assert!(!d1.is_dir()); | |
1815 | assert!(canary.exists()); | |
1816 | } | |
1817 | ||
7453a54e SL |
1818 | #[test] |
1819 | fn recursive_rmdir_of_symlink() { | |
1820 | // test we do not recursively delete a symlink but only dirs. | |
1821 | let tmpdir = tmpdir(); | |
1822 | let link = tmpdir.join("d1"); | |
1823 | let dir = tmpdir.join("d2"); | |
1824 | let canary = dir.join("do_not_delete"); | |
1825 | check!(fs::create_dir_all(&dir)); | |
1826 | check!(check!(File::create(&canary)).write(b"foo")); | |
1827 | check!(symlink_junction(&dir, &link)); | |
1828 | check!(fs::remove_dir_all(&link)); | |
1829 | ||
1830 | assert!(!link.is_dir()); | |
1831 | assert!(canary.exists()); | |
1832 | } | |
1833 | ||
1834 | #[test] | |
1835 | // only Windows makes a distinction between file and directory symlinks. | |
1836 | #[cfg(windows)] | |
1837 | fn recursive_rmdir_of_file_symlink() { | |
1838 | let tmpdir = tmpdir(); | |
1839 | if !got_symlink_permission(&tmpdir) { return }; | |
1840 | ||
1841 | let f1 = tmpdir.join("f1"); | |
1842 | let f2 = tmpdir.join("f2"); | |
1843 | check!(check!(File::create(&f1)).write(b"foo")); | |
1844 | check!(symlink_file(&f1, &f2)); | |
1845 | match fs::remove_dir_all(&f2) { | |
1846 | Ok(..) => panic!("wanted a failure"), | |
1847 | Err(..) => {} | |
1848 | } | |
1849 | } | |
1850 | ||
85aaf69f SL |
1851 | #[test] |
1852 | fn unicode_path_is_dir() { | |
7453a54e SL |
1853 | assert!(Path::new(".").is_dir()); |
1854 | assert!(!Path::new("test/stdtest/fs.rs").is_dir()); | |
85aaf69f SL |
1855 | |
1856 | let tmpdir = tmpdir(); | |
1857 | ||
1858 | let mut dirpath = tmpdir.path().to_path_buf(); | |
92a42be0 | 1859 | dirpath.push("test-가一ー你好"); |
85aaf69f SL |
1860 | check!(fs::create_dir(&dirpath)); |
1861 | assert!(dirpath.is_dir()); | |
1862 | ||
1863 | let mut filepath = dirpath; | |
1864 | filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); | |
1865 | check!(File::create(&filepath)); // ignore return; touch only | |
1866 | assert!(!filepath.is_dir()); | |
1867 | assert!(filepath.exists()); | |
1868 | } | |
1869 | ||
1870 | #[test] | |
1871 | fn unicode_path_exists() { | |
7453a54e SL |
1872 | assert!(Path::new(".").exists()); |
1873 | assert!(!Path::new("test/nonexistent-bogus-path").exists()); | |
85aaf69f SL |
1874 | |
1875 | let tmpdir = tmpdir(); | |
1876 | let unicode = tmpdir.path(); | |
1877 | let unicode = unicode.join(&format!("test-각丁ー再见")); | |
1878 | check!(fs::create_dir(&unicode)); | |
1879 | assert!(unicode.exists()); | |
7453a54e | 1880 | assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); |
85aaf69f SL |
1881 | } |
1882 | ||
1883 | #[test] | |
1884 | fn copy_file_does_not_exist() { | |
7453a54e SL |
1885 | let from = Path::new("test/nonexistent-bogus-path"); |
1886 | let to = Path::new("test/other-bogus-path"); | |
85aaf69f SL |
1887 | |
1888 | match fs::copy(&from, &to) { | |
1889 | Ok(..) => panic!(), | |
1890 | Err(..) => { | |
1891 | assert!(!from.exists()); | |
1892 | assert!(!to.exists()); | |
1893 | } | |
1894 | } | |
1895 | } | |
1896 | ||
c1a9b12d SL |
1897 | #[test] |
1898 | fn copy_src_does_not_exist() { | |
1899 | let tmpdir = tmpdir(); | |
7453a54e | 1900 | let from = Path::new("test/nonexistent-bogus-path"); |
c1a9b12d SL |
1901 | let to = tmpdir.join("out.txt"); |
1902 | check!(check!(File::create(&to)).write(b"hello")); | |
1903 | assert!(fs::copy(&from, &to).is_err()); | |
1904 | assert!(!from.exists()); | |
1905 | let mut v = Vec::new(); | |
1906 | check!(check!(File::open(&to)).read_to_end(&mut v)); | |
1907 | assert_eq!(v, b"hello"); | |
1908 | } | |
1909 | ||
85aaf69f SL |
1910 | #[test] |
1911 | fn copy_file_ok() { | |
1912 | let tmpdir = tmpdir(); | |
1913 | let input = tmpdir.join("in.txt"); | |
1914 | let out = tmpdir.join("out.txt"); | |
1915 | ||
1916 | check!(check!(File::create(&input)).write(b"hello")); | |
1917 | check!(fs::copy(&input, &out)); | |
1918 | let mut v = Vec::new(); | |
1919 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
c34b1796 | 1920 | assert_eq!(v, b"hello"); |
85aaf69f SL |
1921 | |
1922 | assert_eq!(check!(input.metadata()).permissions(), | |
1923 | check!(out.metadata()).permissions()); | |
1924 | } | |
1925 | ||
1926 | #[test] | |
1927 | fn copy_file_dst_dir() { | |
1928 | let tmpdir = tmpdir(); | |
1929 | let out = tmpdir.join("out"); | |
1930 | ||
1931 | check!(File::create(&out)); | |
1932 | match fs::copy(&*out, tmpdir.path()) { | |
1933 | Ok(..) => panic!(), Err(..) => {} | |
1934 | } | |
1935 | } | |
1936 | ||
1937 | #[test] | |
1938 | fn copy_file_dst_exists() { | |
1939 | let tmpdir = tmpdir(); | |
1940 | let input = tmpdir.join("in"); | |
1941 | let output = tmpdir.join("out"); | |
1942 | ||
1943 | check!(check!(File::create(&input)).write("foo".as_bytes())); | |
1944 | check!(check!(File::create(&output)).write("bar".as_bytes())); | |
1945 | check!(fs::copy(&input, &output)); | |
1946 | ||
1947 | let mut v = Vec::new(); | |
1948 | check!(check!(File::open(&output)).read_to_end(&mut v)); | |
1949 | assert_eq!(v, b"foo".to_vec()); | |
1950 | } | |
1951 | ||
1952 | #[test] | |
1953 | fn copy_file_src_dir() { | |
1954 | let tmpdir = tmpdir(); | |
1955 | let out = tmpdir.join("out"); | |
1956 | ||
1957 | match fs::copy(tmpdir.path(), &out) { | |
1958 | Ok(..) => panic!(), Err(..) => {} | |
1959 | } | |
1960 | assert!(!out.exists()); | |
1961 | } | |
1962 | ||
1963 | #[test] | |
1964 | fn copy_file_preserves_perm_bits() { | |
1965 | let tmpdir = tmpdir(); | |
1966 | let input = tmpdir.join("in.txt"); | |
1967 | let out = tmpdir.join("out.txt"); | |
1968 | ||
1969 | let attr = check!(check!(File::create(&input)).metadata()); | |
1970 | let mut p = attr.permissions(); | |
1971 | p.set_readonly(true); | |
1972 | check!(fs::set_permissions(&input, p)); | |
1973 | check!(fs::copy(&input, &out)); | |
1974 | assert!(check!(out.metadata()).permissions().readonly()); | |
c34b1796 AL |
1975 | check!(fs::set_permissions(&input, attr.permissions())); |
1976 | check!(fs::set_permissions(&out, attr.permissions())); | |
85aaf69f SL |
1977 | } |
1978 | ||
c1a9b12d SL |
1979 | #[cfg(windows)] |
1980 | #[test] | |
1981 | fn copy_file_preserves_streams() { | |
1982 | let tmp = tmpdir(); | |
1983 | check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); | |
1984 | assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6); | |
1985 | assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); | |
1986 | let mut v = Vec::new(); | |
1987 | check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); | |
1988 | assert_eq!(v, b"carrot".to_vec()); | |
1989 | } | |
1990 | ||
85aaf69f SL |
1991 | #[test] |
1992 | fn symlinks_work() { | |
1993 | let tmpdir = tmpdir(); | |
7453a54e SL |
1994 | if !got_symlink_permission(&tmpdir) { return }; |
1995 | ||
85aaf69f SL |
1996 | let input = tmpdir.join("in.txt"); |
1997 | let out = tmpdir.join("out.txt"); | |
1998 | ||
1999 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
7453a54e SL |
2000 | check!(symlink_file(&input, &out)); |
2001 | assert!(check!(out.symlink_metadata()).file_type().is_symlink()); | |
85aaf69f SL |
2002 | assert_eq!(check!(fs::metadata(&out)).len(), |
2003 | check!(fs::metadata(&input)).len()); | |
2004 | let mut v = Vec::new(); | |
2005 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
2006 | assert_eq!(v, b"foobar".to_vec()); | |
2007 | } | |
2008 | ||
85aaf69f SL |
2009 | #[test] |
2010 | fn symlink_noexist() { | |
7453a54e SL |
2011 | // Symlinks can point to things that don't exist |
2012 | let tmpdir = tmpdir(); | |
2013 | if !got_symlink_permission(&tmpdir) { return }; | |
2014 | ||
2015 | // Use a relative path for testing. Symlinks get normalized by Windows, | |
2016 | // so we may not get the same path back for absolute paths | |
2017 | check!(symlink_file(&"foo", &tmpdir.join("bar"))); | |
2018 | assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), | |
2019 | "foo"); | |
2020 | } | |
2021 | ||
2022 | #[test] | |
2023 | fn read_link() { | |
2024 | if cfg!(windows) { | |
2025 | // directory symlink | |
2026 | assert_eq!(check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(), | |
2027 | r"C:\ProgramData"); | |
2028 | // junction | |
2029 | assert_eq!(check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(), | |
2030 | r"C:\Users\Default"); | |
2031 | // junction with special permissions | |
2032 | assert_eq!(check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(), | |
2033 | r"C:\Users"); | |
2034 | } | |
85aaf69f | 2035 | let tmpdir = tmpdir(); |
7453a54e SL |
2036 | let link = tmpdir.join("link"); |
2037 | if !got_symlink_permission(&tmpdir) { return }; | |
2038 | check!(symlink_file(&"foo", &link)); | |
2039 | assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo"); | |
85aaf69f SL |
2040 | } |
2041 | ||
2042 | #[test] | |
2043 | fn readlink_not_symlink() { | |
2044 | let tmpdir = tmpdir(); | |
2045 | match fs::read_link(tmpdir.path()) { | |
2046 | Ok(..) => panic!("wanted a failure"), | |
2047 | Err(..) => {} | |
2048 | } | |
2049 | } | |
2050 | ||
2051 | #[test] | |
2052 | fn links_work() { | |
2053 | let tmpdir = tmpdir(); | |
2054 | let input = tmpdir.join("in.txt"); | |
2055 | let out = tmpdir.join("out.txt"); | |
2056 | ||
2057 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
2058 | check!(fs::hard_link(&input, &out)); | |
2059 | assert_eq!(check!(fs::metadata(&out)).len(), | |
2060 | check!(fs::metadata(&input)).len()); | |
2061 | assert_eq!(check!(fs::metadata(&out)).len(), | |
2062 | check!(input.metadata()).len()); | |
2063 | let mut v = Vec::new(); | |
2064 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
2065 | assert_eq!(v, b"foobar".to_vec()); | |
2066 | ||
2067 | // can't link to yourself | |
2068 | match fs::hard_link(&input, &input) { | |
2069 | Ok(..) => panic!("wanted a failure"), | |
2070 | Err(..) => {} | |
2071 | } | |
2072 | // can't link to something that doesn't exist | |
2073 | match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { | |
2074 | Ok(..) => panic!("wanted a failure"), | |
2075 | Err(..) => {} | |
2076 | } | |
2077 | } | |
2078 | ||
2079 | #[test] | |
2080 | fn chmod_works() { | |
2081 | let tmpdir = tmpdir(); | |
2082 | let file = tmpdir.join("in.txt"); | |
2083 | ||
2084 | check!(File::create(&file)); | |
2085 | let attr = check!(fs::metadata(&file)); | |
2086 | assert!(!attr.permissions().readonly()); | |
2087 | let mut p = attr.permissions(); | |
2088 | p.set_readonly(true); | |
2089 | check!(fs::set_permissions(&file, p.clone())); | |
2090 | let attr = check!(fs::metadata(&file)); | |
2091 | assert!(attr.permissions().readonly()); | |
2092 | ||
c34b1796 AL |
2093 | match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { |
2094 | Ok(..) => panic!("wanted an error"), | |
85aaf69f SL |
2095 | Err(..) => {} |
2096 | } | |
c34b1796 AL |
2097 | |
2098 | p.set_readonly(false); | |
2099 | check!(fs::set_permissions(&file, p)); | |
85aaf69f SL |
2100 | } |
2101 | ||
2102 | #[test] | |
2103 | fn sync_doesnt_kill_anything() { | |
2104 | let tmpdir = tmpdir(); | |
2105 | let path = tmpdir.join("in.txt"); | |
2106 | ||
2107 | let mut file = check!(File::create(&path)); | |
2108 | check!(file.sync_all()); | |
2109 | check!(file.sync_data()); | |
2110 | check!(file.write(b"foo")); | |
2111 | check!(file.sync_all()); | |
2112 | check!(file.sync_data()); | |
2113 | } | |
2114 | ||
2115 | #[test] | |
2116 | fn truncate_works() { | |
2117 | let tmpdir = tmpdir(); | |
2118 | let path = tmpdir.join("in.txt"); | |
2119 | ||
2120 | let mut file = check!(File::create(&path)); | |
2121 | check!(file.write(b"foo")); | |
2122 | check!(file.sync_all()); | |
2123 | ||
2124 | // Do some simple things with truncation | |
2125 | assert_eq!(check!(file.metadata()).len(), 3); | |
2126 | check!(file.set_len(10)); | |
2127 | assert_eq!(check!(file.metadata()).len(), 10); | |
2128 | check!(file.write(b"bar")); | |
2129 | check!(file.sync_all()); | |
2130 | assert_eq!(check!(file.metadata()).len(), 10); | |
2131 | ||
2132 | let mut v = Vec::new(); | |
2133 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
2134 | assert_eq!(v, b"foobar\0\0\0\0".to_vec()); | |
2135 | ||
2136 | // Truncate to a smaller length, don't seek, and then write something. | |
c34b1796 | 2137 | // Ensure that the intermediate zeroes are all filled in (we have `seek`ed |
85aaf69f SL |
2138 | // past the end of the file). |
2139 | check!(file.set_len(2)); | |
2140 | assert_eq!(check!(file.metadata()).len(), 2); | |
2141 | check!(file.write(b"wut")); | |
2142 | check!(file.sync_all()); | |
2143 | assert_eq!(check!(file.metadata()).len(), 9); | |
2144 | let mut v = Vec::new(); | |
2145 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
2146 | assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); | |
2147 | } | |
2148 | ||
2149 | #[test] | |
2150 | fn open_flavors() { | |
2151 | use fs::OpenOptions as OO; | |
2152 | fn c<T: Clone>(t: &T) -> T { t.clone() } | |
2153 | ||
2154 | let tmpdir = tmpdir(); | |
2155 | ||
2156 | let mut r = OO::new(); r.read(true); | |
2157 | let mut w = OO::new(); w.write(true); | |
7453a54e SL |
2158 | let mut rw = OO::new(); rw.read(true).write(true); |
2159 | let mut a = OO::new(); a.append(true); | |
2160 | let mut ra = OO::new(); ra.read(true).append(true); | |
2161 | ||
2162 | let invalid_options = if cfg!(windows) { "The parameter is incorrect" } | |
2163 | else { "Invalid argument" }; | |
2164 | ||
2165 | // Test various combinations of creation modes and access modes. | |
2166 | // | |
2167 | // Allowed: | |
2168 | // creation mode | read | write | read-write | append | read-append | | |
2169 | // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:| | |
2170 | // not set (open existing) | X | X | X | X | X | | |
2171 | // create | | X | X | X | X | | |
2172 | // truncate | | X | X | | | | |
2173 | // create and truncate | | X | X | | | | |
2174 | // create_new | | X | X | X | X | | |
2175 | // | |
2176 | // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it. | |
2177 | ||
2178 | // write-only | |
2179 | check!(c(&w).create_new(true).open(&tmpdir.join("a"))); | |
2180 | check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a"))); | |
2181 | check!(c(&w).truncate(true).open(&tmpdir.join("a"))); | |
2182 | check!(c(&w).create(true).open(&tmpdir.join("a"))); | |
2183 | check!(c(&w).open(&tmpdir.join("a"))); | |
2184 | ||
2185 | // read-only | |
2186 | error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options); | |
2187 | error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options); | |
2188 | error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options); | |
2189 | error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options); | |
2190 | check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only | |
2191 | ||
2192 | // read-write | |
2193 | check!(c(&rw).create_new(true).open(&tmpdir.join("c"))); | |
2194 | check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c"))); | |
2195 | check!(c(&rw).truncate(true).open(&tmpdir.join("c"))); | |
85aaf69f | 2196 | check!(c(&rw).create(true).open(&tmpdir.join("c"))); |
7453a54e SL |
2197 | check!(c(&rw).open(&tmpdir.join("c"))); |
2198 | ||
2199 | // append | |
2200 | check!(c(&a).create_new(true).open(&tmpdir.join("d"))); | |
2201 | error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); | |
2202 | error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); | |
2203 | check!(c(&a).create(true).open(&tmpdir.join("d"))); | |
2204 | check!(c(&a).open(&tmpdir.join("d"))); | |
2205 | ||
2206 | // read-append | |
2207 | check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); | |
2208 | error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); | |
2209 | error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); | |
2210 | check!(c(&ra).create(true).open(&tmpdir.join("e"))); | |
2211 | check!(c(&ra).open(&tmpdir.join("e"))); | |
2212 | ||
2213 | // Test opening a file without setting an access mode | |
2214 | let mut blank = OO::new(); | |
2215 | error!(blank.create(true).open(&tmpdir.join("f")), invalid_options); | |
2216 | ||
2217 | // Test write works | |
2218 | check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes())); | |
2219 | ||
2220 | // Test write fails for read-only | |
85aaf69f SL |
2221 | check!(r.open(&tmpdir.join("h"))); |
2222 | { | |
2223 | let mut f = check!(r.open(&tmpdir.join("h"))); | |
2224 | assert!(f.write("wut".as_bytes()).is_err()); | |
2225 | } | |
7453a54e SL |
2226 | |
2227 | // Test write overwrites | |
2228 | { | |
2229 | let mut f = check!(c(&w).open(&tmpdir.join("h"))); | |
2230 | check!(f.write("baz".as_bytes())); | |
2231 | } | |
2232 | { | |
2233 | let mut f = check!(c(&r).open(&tmpdir.join("h"))); | |
2234 | let mut b = vec![0; 6]; | |
2235 | check!(f.read(&mut b)); | |
2236 | assert_eq!(b, "bazbar".as_bytes()); | |
2237 | } | |
2238 | ||
2239 | // Test truncate works | |
2240 | { | |
2241 | let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); | |
2242 | check!(f.write("foo".as_bytes())); | |
2243 | } | |
2244 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); | |
2245 | ||
2246 | // Test append works | |
85aaf69f SL |
2247 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); |
2248 | { | |
7453a54e | 2249 | let mut f = check!(c(&a).open(&tmpdir.join("h"))); |
85aaf69f SL |
2250 | check!(f.write("bar".as_bytes())); |
2251 | } | |
2252 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); | |
7453a54e SL |
2253 | |
2254 | // Test .append(true) equals .write(true).append(true) | |
85aaf69f | 2255 | { |
7453a54e SL |
2256 | let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); |
2257 | check!(f.write("baz".as_bytes())); | |
85aaf69f | 2258 | } |
7453a54e SL |
2259 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); |
2260 | } | |
2261 | ||
2262 | #[test] | |
2263 | fn _assert_send_sync() { | |
2264 | fn _assert_send_sync<T: Send + Sync>() {} | |
2265 | _assert_send_sync::<OpenOptions>(); | |
85aaf69f SL |
2266 | } |
2267 | ||
85aaf69f SL |
2268 | #[test] |
2269 | fn binary_file() { | |
2270 | let mut bytes = [0; 1024]; | |
c34b1796 | 2271 | StdRng::new().unwrap().fill_bytes(&mut bytes); |
85aaf69f SL |
2272 | |
2273 | let tmpdir = tmpdir(); | |
2274 | ||
2275 | check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); | |
2276 | let mut v = Vec::new(); | |
2277 | check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); | |
c34b1796 | 2278 | assert!(v == &bytes[..]); |
85aaf69f SL |
2279 | } |
2280 | ||
7453a54e SL |
2281 | #[test] |
2282 | fn file_try_clone() { | |
2283 | let tmpdir = tmpdir(); | |
2284 | ||
2285 | let mut f1 = check!(OpenOptions::new() | |
2286 | .read(true) | |
2287 | .write(true) | |
2288 | .create(true) | |
2289 | .open(&tmpdir.join("test"))); | |
2290 | let mut f2 = check!(f1.try_clone()); | |
2291 | ||
2292 | check!(f1.write_all(b"hello world")); | |
2293 | check!(f1.seek(SeekFrom::Start(2))); | |
2294 | ||
2295 | let mut buf = vec![]; | |
2296 | check!(f2.read_to_end(&mut buf)); | |
2297 | assert_eq!(buf, b"llo world"); | |
2298 | drop(f2); | |
2299 | ||
2300 | check!(f1.write_all(b"!")); | |
2301 | } | |
2302 | ||
85aaf69f | 2303 | #[test] |
c34b1796 | 2304 | #[cfg(not(windows))] |
85aaf69f SL |
2305 | fn unlink_readonly() { |
2306 | let tmpdir = tmpdir(); | |
2307 | let path = tmpdir.join("file"); | |
2308 | check!(File::create(&path)); | |
2309 | let mut perm = check!(fs::metadata(&path)).permissions(); | |
2310 | perm.set_readonly(true); | |
2311 | check!(fs::set_permissions(&path, perm)); | |
2312 | check!(fs::remove_file(&path)); | |
2313 | } | |
c34b1796 AL |
2314 | |
2315 | #[test] | |
2316 | fn mkdir_trailing_slash() { | |
2317 | let tmpdir = tmpdir(); | |
2318 | let path = tmpdir.join("file"); | |
2319 | check!(fs::create_dir_all(&path.join("a/"))); | |
2320 | } | |
d9579d0f | 2321 | |
b039eaaf SL |
2322 | #[test] |
2323 | fn canonicalize_works_simple() { | |
2324 | let tmpdir = tmpdir(); | |
2325 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
2326 | let file = tmpdir.join("test"); | |
2327 | File::create(&file).unwrap(); | |
2328 | assert_eq!(fs::canonicalize(&file).unwrap(), file); | |
2329 | } | |
2330 | ||
d9579d0f | 2331 | #[test] |
d9579d0f AL |
2332 | fn realpath_works() { |
2333 | let tmpdir = tmpdir(); | |
7453a54e SL |
2334 | if !got_symlink_permission(&tmpdir) { return }; |
2335 | ||
d9579d0f AL |
2336 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); |
2337 | let file = tmpdir.join("test"); | |
2338 | let dir = tmpdir.join("test2"); | |
2339 | let link = dir.join("link"); | |
2340 | let linkdir = tmpdir.join("test3"); | |
2341 | ||
2342 | File::create(&file).unwrap(); | |
2343 | fs::create_dir(&dir).unwrap(); | |
7453a54e SL |
2344 | symlink_file(&file, &link).unwrap(); |
2345 | symlink_dir(&dir, &linkdir).unwrap(); | |
d9579d0f AL |
2346 | |
2347 | assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); | |
2348 | ||
2349 | assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); | |
2350 | assert_eq!(fs::canonicalize(&file).unwrap(), file); | |
2351 | assert_eq!(fs::canonicalize(&link).unwrap(), file); | |
2352 | assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); | |
2353 | assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); | |
2354 | } | |
2355 | ||
2356 | #[test] | |
d9579d0f AL |
2357 | fn realpath_works_tricky() { |
2358 | let tmpdir = tmpdir(); | |
7453a54e | 2359 | if !got_symlink_permission(&tmpdir) { return }; |
d9579d0f | 2360 | |
7453a54e | 2361 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); |
d9579d0f AL |
2362 | let a = tmpdir.join("a"); |
2363 | let b = a.join("b"); | |
2364 | let c = b.join("c"); | |
2365 | let d = a.join("d"); | |
2366 | let e = d.join("e"); | |
2367 | let f = a.join("f"); | |
2368 | ||
2369 | fs::create_dir_all(&b).unwrap(); | |
2370 | fs::create_dir_all(&d).unwrap(); | |
2371 | File::create(&f).unwrap(); | |
7453a54e SL |
2372 | if cfg!(not(windows)) { |
2373 | symlink_dir("../d/e", &c).unwrap(); | |
2374 | symlink_file("../f", &e).unwrap(); | |
2375 | } | |
2376 | if cfg!(windows) { | |
2377 | symlink_dir(r"..\d\e", &c).unwrap(); | |
2378 | symlink_file(r"..\f", &e).unwrap(); | |
2379 | } | |
d9579d0f AL |
2380 | |
2381 | assert_eq!(fs::canonicalize(&c).unwrap(), f); | |
2382 | assert_eq!(fs::canonicalize(&e).unwrap(), f); | |
2383 | } | |
2384 | ||
2385 | #[test] | |
2386 | fn dir_entry_methods() { | |
2387 | let tmpdir = tmpdir(); | |
2388 | ||
2389 | fs::create_dir_all(&tmpdir.join("a")).unwrap(); | |
2390 | File::create(&tmpdir.join("b")).unwrap(); | |
2391 | ||
2392 | for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { | |
2393 | let fname = file.file_name(); | |
2394 | match fname.to_str() { | |
2395 | Some("a") => { | |
2396 | assert!(file.file_type().unwrap().is_dir()); | |
2397 | assert!(file.metadata().unwrap().is_dir()); | |
2398 | } | |
2399 | Some("b") => { | |
2400 | assert!(file.file_type().unwrap().is_file()); | |
2401 | assert!(file.metadata().unwrap().is_file()); | |
2402 | } | |
2403 | f => panic!("unknown file name: {:?}", f), | |
2404 | } | |
2405 | } | |
2406 | } | |
b039eaaf SL |
2407 | |
2408 | #[test] | |
2409 | fn read_dir_not_found() { | |
2410 | let res = fs::read_dir("/path/that/does/not/exist"); | |
2411 | assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); | |
2412 | } | |
7453a54e SL |
2413 | |
2414 | #[test] | |
2415 | fn create_dir_all_with_junctions() { | |
2416 | let tmpdir = tmpdir(); | |
2417 | let target = tmpdir.join("target"); | |
2418 | ||
2419 | let junction = tmpdir.join("junction"); | |
2420 | let b = junction.join("a/b"); | |
2421 | ||
2422 | let link = tmpdir.join("link"); | |
2423 | let d = link.join("c/d"); | |
2424 | ||
2425 | fs::create_dir(&target).unwrap(); | |
2426 | ||
2427 | check!(symlink_junction(&target, &junction)); | |
2428 | check!(fs::create_dir_all(&b)); | |
2429 | // the junction itself is not a directory, but `is_dir()` on a Path | |
2430 | // follows links | |
2431 | assert!(junction.is_dir()); | |
2432 | assert!(b.exists()); | |
2433 | ||
2434 | if !got_symlink_permission(&tmpdir) { return }; | |
2435 | check!(symlink_dir(&target, &link)); | |
2436 | check!(fs::create_dir_all(&d)); | |
2437 | assert!(link.is_dir()); | |
2438 | assert!(d.exists()); | |
2439 | } | |
2440 | ||
2441 | #[test] | |
2442 | fn metadata_access_times() { | |
2443 | let tmpdir = tmpdir(); | |
2444 | ||
2445 | let b = tmpdir.join("b"); | |
2446 | File::create(&b).unwrap(); | |
2447 | ||
2448 | let a = check!(fs::metadata(&tmpdir.path())); | |
2449 | let b = check!(fs::metadata(&b)); | |
2450 | ||
2451 | assert_eq!(check!(a.accessed()), check!(a.accessed())); | |
2452 | assert_eq!(check!(a.modified()), check!(a.modified())); | |
2453 | assert_eq!(check!(b.accessed()), check!(b.modified())); | |
2454 | ||
2455 | if cfg!(target_os = "macos") || cfg!(target_os = "windows") { | |
2456 | check!(a.created()); | |
2457 | check!(b.created()); | |
2458 | } | |
2459 | } | |
85aaf69f | 2460 | } |