1 use crate::fs::{asyncify, File}; 2 3 use std::io; 4 use std::path::Path; 5 6 /// Options and flags which can be used to configure how a file is opened. 7 /// 8 /// This builder exposes the ability to configure how a [`File`] is opened and 9 /// what operations are permitted on the open file. The [`File::open`] and 10 /// [`File::create`] methods are aliases for commonly used options using this 11 /// builder. 12 /// 13 /// Generally speaking, when using `OpenOptions`, you'll first call [`new`], 14 /// then chain calls to methods to set each option, then call [`open`], passing 15 /// the path of the file you're trying to open. This will give you a 16 /// [`io::Result`][result] with a [`File`] inside that you can further operate 17 /// on. 18 /// 19 /// This is a specialized version of [`std::fs::OpenOptions`] for usage from 20 /// the Tokio runtime. 21 /// 22 /// `From<std::fs::OpenOptions>` is implemented for more advanced configuration 23 /// than the methods provided here. 24 /// 25 /// [`new`]: OpenOptions::new 26 /// [`open`]: OpenOptions::open 27 /// [result]: std::io::Result 28 /// [`File`]: File 29 /// [`File::open`]: File::open 30 /// [`File::create`]: File::create 31 /// [`std::fs::OpenOptions`]: std::fs::OpenOptions 32 /// 33 /// # Examples 34 /// 35 /// Opening a file to read: 36 /// 37 /// ```no_run 38 /// use tokio::fs::OpenOptions; 39 /// use std::io; 40 /// 41 /// #[tokio::main] 42 /// async fn main() -> io::Result<()> { 43 /// let file = OpenOptions::new() 44 /// .read(true) 45 /// .open("foo.txt") 46 /// .await?; 47 /// 48 /// Ok(()) 49 /// } 50 /// ``` 51 /// 52 /// Opening a file for both reading and writing, as well as creating it if it 53 /// doesn't exist: 54 /// 55 /// ```no_run 56 /// use tokio::fs::OpenOptions; 57 /// use std::io; 58 /// 59 /// #[tokio::main] 60 /// async fn main() -> io::Result<()> { 61 /// let file = OpenOptions::new() 62 /// .read(true) 63 /// .write(true) 64 /// .create(true) 65 /// .open("foo.txt") 66 /// .await?; 67 /// 68 /// Ok(()) 69 /// } 70 /// ``` 71 #[derive(Clone, Debug)] 72 pub struct OpenOptions(std::fs::OpenOptions); 73 74 impl OpenOptions { 75 /// Creates a blank new set of options ready for configuration. 76 /// 77 /// All options are initially set to `false`. 78 /// 79 /// This is an async version of [`std::fs::OpenOptions::new`][std] 80 /// 81 /// [std]: std::fs::OpenOptions::new 82 /// 83 /// # Examples 84 /// 85 /// ```no_run 86 /// use tokio::fs::OpenOptions; 87 /// 88 /// let mut options = OpenOptions::new(); 89 /// let future = options.read(true).open("foo.txt"); 90 /// ``` new() -> OpenOptions91 pub fn new() -> OpenOptions { 92 OpenOptions(std::fs::OpenOptions::new()) 93 } 94 95 /// Sets the option for read access. 96 /// 97 /// This option, when true, will indicate that the file should be 98 /// `read`-able if opened. 99 /// 100 /// This is an async version of [`std::fs::OpenOptions::read`][std] 101 /// 102 /// [std]: std::fs::OpenOptions::read 103 /// 104 /// # Examples 105 /// 106 /// ```no_run 107 /// use tokio::fs::OpenOptions; 108 /// use std::io; 109 /// 110 /// #[tokio::main] 111 /// async fn main() -> io::Result<()> { 112 /// let file = OpenOptions::new() 113 /// .read(true) 114 /// .open("foo.txt") 115 /// .await?; 116 /// 117 /// Ok(()) 118 /// } 119 /// ``` read(&mut self, read: bool) -> &mut OpenOptions120 pub fn read(&mut self, read: bool) -> &mut OpenOptions { 121 self.0.read(read); 122 self 123 } 124 125 /// Sets the option for write access. 126 /// 127 /// This option, when true, will indicate that the file should be 128 /// `write`-able if opened. 129 /// 130 /// This is an async version of [`std::fs::OpenOptions::write`][std] 131 /// 132 /// [std]: std::fs::OpenOptions::write 133 /// 134 /// # Examples 135 /// 136 /// ```no_run 137 /// use tokio::fs::OpenOptions; 138 /// use std::io; 139 /// 140 /// #[tokio::main] 141 /// async fn main() -> io::Result<()> { 142 /// let file = OpenOptions::new() 143 /// .write(true) 144 /// .open("foo.txt") 145 /// .await?; 146 /// 147 /// Ok(()) 148 /// } 149 /// ``` write(&mut self, write: bool) -> &mut OpenOptions150 pub fn write(&mut self, write: bool) -> &mut OpenOptions { 151 self.0.write(write); 152 self 153 } 154 155 /// Sets the option for the append mode. 156 /// 157 /// This option, when true, means that writes will append to a file instead 158 /// of overwriting previous contents. Note that setting 159 /// `.write(true).append(true)` has the same effect as setting only 160 /// `.append(true)`. 161 /// 162 /// For most filesystems, the operating system guarantees that all writes are 163 /// atomic: no writes get mangled because another process writes at the same 164 /// time. 165 /// 166 /// One maybe obvious note when using append-mode: make sure that all data 167 /// that belongs together is written to the file in one operation. This 168 /// can be done by concatenating strings before passing them to [`write()`], 169 /// or using a buffered writer (with a buffer of adequate size), 170 /// and calling [`flush()`] when the message is complete. 171 /// 172 /// If a file is opened with both read and append access, beware that after 173 /// opening, and after every write, the position for reading may be set at the 174 /// end of the file. So, before writing, save the current position (using 175 /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read. 176 /// 177 /// This is an async version of [`std::fs::OpenOptions::append`][std] 178 /// 179 /// [std]: std::fs::OpenOptions::append 180 /// 181 /// ## Note 182 /// 183 /// This function doesn't create the file if it doesn't exist. Use the [`create`] 184 /// method to do so. 185 /// 186 /// [`write()`]: crate::io::AsyncWriteExt::write 187 /// [`flush()`]: crate::io::AsyncWriteExt::flush 188 /// [`seek`]: crate::io::AsyncSeekExt::seek 189 /// [`SeekFrom`]: std::io::SeekFrom 190 /// [`Current`]: std::io::SeekFrom::Current 191 /// [`create`]: OpenOptions::create 192 /// 193 /// # Examples 194 /// 195 /// ```no_run 196 /// use tokio::fs::OpenOptions; 197 /// use std::io; 198 /// 199 /// #[tokio::main] 200 /// async fn main() -> io::Result<()> { 201 /// let file = OpenOptions::new() 202 /// .append(true) 203 /// .open("foo.txt") 204 /// .await?; 205 /// 206 /// Ok(()) 207 /// } 208 /// ``` append(&mut self, append: bool) -> &mut OpenOptions209 pub fn append(&mut self, append: bool) -> &mut OpenOptions { 210 self.0.append(append); 211 self 212 } 213 214 /// Sets the option for truncating a previous file. 215 /// 216 /// If a file is successfully opened with this option set it will truncate 217 /// the file to 0 length if it already exists. 218 /// 219 /// The file must be opened with write access for truncate to work. 220 /// 221 /// This is an async version of [`std::fs::OpenOptions::truncate`][std] 222 /// 223 /// [std]: std::fs::OpenOptions::truncate 224 /// 225 /// # Examples 226 /// 227 /// ```no_run 228 /// use tokio::fs::OpenOptions; 229 /// use std::io; 230 /// 231 /// #[tokio::main] 232 /// async fn main() -> io::Result<()> { 233 /// let file = OpenOptions::new() 234 /// .write(true) 235 /// .truncate(true) 236 /// .open("foo.txt") 237 /// .await?; 238 /// 239 /// Ok(()) 240 /// } 241 /// ``` truncate(&mut self, truncate: bool) -> &mut OpenOptions242 pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { 243 self.0.truncate(truncate); 244 self 245 } 246 247 /// Sets the option for creating a new file. 248 /// 249 /// This option indicates whether a new file will be created if the file 250 /// does not yet already exist. 251 /// 252 /// In order for the file to be created, [`write`] or [`append`] access must 253 /// be used. 254 /// 255 /// This is an async version of [`std::fs::OpenOptions::create`][std] 256 /// 257 /// [std]: std::fs::OpenOptions::create 258 /// [`write`]: OpenOptions::write 259 /// [`append`]: OpenOptions::append 260 /// 261 /// # Examples 262 /// 263 /// ```no_run 264 /// use tokio::fs::OpenOptions; 265 /// use std::io; 266 /// 267 /// #[tokio::main] 268 /// async fn main() -> io::Result<()> { 269 /// let file = OpenOptions::new() 270 /// .write(true) 271 /// .create(true) 272 /// .open("foo.txt") 273 /// .await?; 274 /// 275 /// Ok(()) 276 /// } 277 /// ``` create(&mut self, create: bool) -> &mut OpenOptions278 pub fn create(&mut self, create: bool) -> &mut OpenOptions { 279 self.0.create(create); 280 self 281 } 282 283 /// Sets the option to always create a new file. 284 /// 285 /// This option indicates whether a new file will be created. No file is 286 /// allowed to exist at the target location, also no (dangling) symlink. 287 /// 288 /// This option is useful because it is atomic. Otherwise between checking 289 /// whether a file exists and creating a new one, the file may have been 290 /// created by another process (a TOCTOU race condition / attack). 291 /// 292 /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are 293 /// ignored. 294 /// 295 /// The file must be opened with write or append access in order to create a 296 /// new file. 297 /// 298 /// This is an async version of [`std::fs::OpenOptions::create_new`][std] 299 /// 300 /// [std]: std::fs::OpenOptions::create_new 301 /// [`.create()`]: OpenOptions::create 302 /// [`.truncate()`]: OpenOptions::truncate 303 /// 304 /// # Examples 305 /// 306 /// ```no_run 307 /// use tokio::fs::OpenOptions; 308 /// use std::io; 309 /// 310 /// #[tokio::main] 311 /// async fn main() -> io::Result<()> { 312 /// let file = OpenOptions::new() 313 /// .write(true) 314 /// .create_new(true) 315 /// .open("foo.txt") 316 /// .await?; 317 /// 318 /// Ok(()) 319 /// } 320 /// ``` create_new(&mut self, create_new: bool) -> &mut OpenOptions321 pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { 322 self.0.create_new(create_new); 323 self 324 } 325 326 /// Opens a file at `path` with the options specified by `self`. 327 /// 328 /// This is an async version of [`std::fs::OpenOptions::open`][std] 329 /// 330 /// [std]: std::fs::OpenOptions::open 331 /// 332 /// # Errors 333 /// 334 /// This function will return an error under a number of different 335 /// circumstances. Some of these error conditions are listed here, together 336 /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of 337 /// the compatibility contract of the function, especially the `Other` kind 338 /// might change to more specific kinds in the future. 339 /// 340 /// * [`NotFound`]: The specified file does not exist and neither `create` 341 /// or `create_new` is set. 342 /// * [`NotFound`]: One of the directory components of the file path does 343 /// not exist. 344 /// * [`PermissionDenied`]: The user lacks permission to get the specified 345 /// access rights for the file. 346 /// * [`PermissionDenied`]: The user lacks permission to open one of the 347 /// directory components of the specified path. 348 /// * [`AlreadyExists`]: `create_new` was specified and the file already 349 /// exists. 350 /// * [`InvalidInput`]: Invalid combinations of open options (truncate 351 /// without write access, no access mode set, etc.). 352 /// * [`Other`]: One of the directory components of the specified file path 353 /// was not, in fact, a directory. 354 /// * [`Other`]: Filesystem-level errors: full disk, write permission 355 /// requested on a read-only file system, exceeded disk quota, too many 356 /// open files, too long filename, too many symbolic links in the 357 /// specified path (Unix-like systems only), etc. 358 /// 359 /// # Examples 360 /// 361 /// ```no_run 362 /// use tokio::fs::OpenOptions; 363 /// use std::io; 364 /// 365 /// #[tokio::main] 366 /// async fn main() -> io::Result<()> { 367 /// let file = OpenOptions::new().open("foo.txt").await?; 368 /// Ok(()) 369 /// } 370 /// ``` 371 /// 372 /// [`ErrorKind`]: std::io::ErrorKind 373 /// [`AlreadyExists`]: std::io::ErrorKind::AlreadyExists 374 /// [`InvalidInput`]: std::io::ErrorKind::InvalidInput 375 /// [`NotFound`]: std::io::ErrorKind::NotFound 376 /// [`Other`]: std::io::ErrorKind::Other 377 /// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied open(&self, path: impl AsRef<Path>) -> io::Result<File>378 pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> { 379 let path = path.as_ref().to_owned(); 380 let opts = self.0.clone(); 381 382 let std = asyncify(move || opts.open(path)).await?; 383 Ok(File::from_std(std)) 384 } 385 386 /// Returns a mutable reference to the the underlying std::fs::OpenOptions 387 #[cfg(unix)] as_inner_mut(&mut self) -> &mut std::fs::OpenOptions388 pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions { 389 &mut self.0 390 } 391 } 392 393 impl From<std::fs::OpenOptions> for OpenOptions { from(options: std::fs::OpenOptions) -> OpenOptions394 fn from(options: std::fs::OpenOptions) -> OpenOptions { 395 OpenOptions(options) 396 } 397 } 398 399 impl Default for OpenOptions { default() -> Self400 fn default() -> Self { 401 Self::new() 402 } 403 } 404