1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
//! Cross-platform implementations of fs methods that don't do anything //! special. Fallback intended for use when a platform does not provide //! enhanced functionality. use std::{io, fs, path, ffi}; use std::cmp::PartialEq; use crate::{tar, tape, spanning}; use crate::tuning::Configuration; /// Supertrait that represents all the things a good archive sink needs to be. /// /// TODO: The **moment** Rust gets the ability to handle multiple traits in a /// single trait object, delete this arbitrary supertrait immediately. /// /// TODO: wait no now this supertrait does downcasts because Box won't pub trait ArchivalSink<I>: Send + io::Write + spanning::RecoverableWrite<I> { fn downcast_seek(&mut self) -> Option<&mut dyn io::Seek> { None } fn downcast_tapedevice(&mut self) -> Option<&mut dyn tape::TapeDevice> { None } } impl<I> ArchivalSink<I> for fs::File { fn downcast_seek(&mut self) -> Option<&mut dyn io::Seek> { Some(self) } } /// Open a sink object for writing an archive (aka "tape"). /// /// # Parameters /// /// This function accepts the name of an output device and a blocking factor. /// The output device's name must be interpreted within the operating system's /// usual namespace for files and devices. /// /// ## Blocking /// /// Certain kinds of output devices are *record-oriented* and can be written to /// in units of records. Notably, this includes tape devices. If blocking is /// requested, then all writes will be buffered into records of this size, such /// that the given data consists of a number of fixed records. (This includes /// padding with nulls at the end of the file.) If the given device is not a /// record-oriented device, or blocking is not requested, then a normal writer /// will be constructed. /// /// If your device supports records of variable length, requesting a blocking /// factor of None will cause each write to the device to create a new record /// of the given size. /// /// # Returns /// /// If the path given in outfile names a valid object of some kind that can be /// written to, it will be opened and returned. Otherwise yields an error. /// /// open_sink is permitted to return writers that write to any source, /// including but not limited to: /// /// - Files /// - Standard output /// - Magnetic tape drives /// - Serial ports /// - Nothing (e.g. /dev/null) /// /// The only restriction is that such devices must exist within a platform /// specified namespace and that outfile must name such a device within that /// namespace. It is not permitted to implement nonstandard paths for accessing /// other kinds of devices not normally exposed through a device or file /// namespace, except in the case where the platform implements separate and /// disjoint namespaces for each. /// /// Due to the wide variety of sink devices, this function only returns /// `io::Write`. For more specific access, consider using another function to /// obtain a more suitable boxed trait object. /// /// # Platform considerations /// /// This is the portable version of the function. It supports writes to files /// only. Platform-specific sink functions may support opening other kinds of /// writers. #[allow(unused_variables)] pub fn open_sink<P: AsRef<path::Path>, I>(outfile: P, tuning: &Configuration, limit: Option<u64>) -> io::Result<Box<ArchivalSink<I>>> where ffi::OsString: From<P>, P: Clone, I: 'static + Send + Clone + PartialEq { let file = fs::File::create(outfile.as_ref())?; if let Some(limit) = limit { Ok(Box::new(spanning::LimitingWriter::wrap(file, limit))) } else { Ok(Box::new(file)) } } /// Open an object for total control of a tape device. /// /// # Parameters /// /// This function accepts the name of an output device corresponding to a tape /// device. The namespace exposed by `open_tape` must be identical to that of /// `open_sink`, at least in the case where such names within the space /// correspond to tape devices. /// /// (e.g. `open_sink("/dev/nst0")` must match `open_tape("/dev/nst0")`, but /// `open_tape("/dev/sda0")` is allowed to error.) /// /// # Returns /// /// If the path given in outfile names a valid tape device, a boxed /// `tape::TapeDevice` will be returned by which you can control the tape. /// /// It is implementation-defined whether it is allowed to open a tape device /// twice. To avoid having to do that, `tape::TapeDevice` conveniently inherits /// `io::Write`. /// /// # Platform considerations /// /// This is the portable version of the function. Since portable tape access /// isn't a thing that makes sense, this function only returns errors. pub fn open_tape<P: AsRef<path::Path>>(_tapedev: P) -> io::Result<Box<tape::TapeDevice>> where ffi::OsString: From<P>, P: Clone { Err(io::Error::new(io::ErrorKind::Other, "Magnetic tape control is not implemented for this operating system.")) } /// Given a directory entry, produce valid Unix mode bits for it. /// /// # Parameters /// /// This function accepts one parameter, the metadata to be converted into mode /// bits. /// /// # Returns /// /// If no errors occured, yields a valid Unix mode bit. /// /// # Platform considerations /// /// This is the portable version of the function. It creates a plausible set of /// mode bits for platforms that either don't provide filesystem security, or /// provide different security notions than what Unix supports. /// /// Operating systems with security metadata of a different format may attempt /// to emulate Unix mode bits. Such emulation is acceptable so long as the /// permissions faithfully reflect actual read and write permissions granted to /// one filesystem user, one filesystem group, and all other users on the /// system. The security principals chosen for mode bit emulation may be /// arbitrarily selected, subject to the following restrictions: /// /// - The given user has ownership rights over the given file, e.g., has /// permission to grant or revoke permissions to others. /// - The given group is a security principal with those given permissions. /// - The other bits faithfully represent the permissions afforded to every /// user on the system, or failing that, the least privileged user on the /// system. /// /// TODO: Make a Windows (NT?) version of this that queries the Security API to /// produce plausible mode bits. pub fn get_unix_mode(metadata: &fs::Metadata) -> io::Result<u32> { if !metadata.is_dir() { if metadata.permissions().readonly() { Ok(0o444) } else { Ok(0o644) } } else { if metadata.permissions().readonly() { Ok(0o555) } else { Ok(0o755) } } } /// Given some metadata, produce a valid tar file type for it. /// /// # Parameters /// /// This function accepts one parameter, the metadata to be converted into mode /// bits. /// /// # Returns /// /// If no errors occured, yields a valid filetype as defined by the abstract /// filetype enum within the `tar` module. /// /// # Platform considerations /// /// This is the portable version of the function. It will always indicate a /// directory, a file, or a symbolic link. It may error if the platform /// implementation of `fs::Metadata` indicates none of the given file types /// apply; however, this is a violation of Rust's specifications. pub fn get_file_type(metadata: &fs::Metadata) -> io::Result<tar::header::TarFileType> { if metadata.file_type().is_dir() { Ok(tar::header::TarFileType::Directory) } else if metadata.file_type().is_file() { Ok(tar::header::TarFileType::FileStream) } else if metadata.file_type().is_symlink() { Ok(tar::header::TarFileType::SymbolicLink) } else { Err(io::Error::new(io::ErrorKind::InvalidInput, "Metadata did not yield any valid file type for tarball")) } } /// Determine the UNIX owner ID and name for a given file. /// /// # Parameters /// /// This function accepts two parameters, the metadata to be read and the path /// which generated the metadata. /// /// # Returns /// /// If no errors occured, yields a tuple of the user's ID and name. /// /// # Platform considerations /// /// This is the portable version of the function. It will always indicate that /// all files are owned by root. pub fn get_unix_owner(_metadata: &fs::Metadata, _path: &path::Path) -> io::Result<(u32, String)> { Ok((0, "root".to_string())) } /// Determine the UNIX group ID and name for a given file. /// /// # Parameters /// /// This function accepts two parameters, the metadata to be read and the path /// which generated the metadata. /// /// # Returns /// /// If no errors occured, yields a tuple of the group's ID and name. /// /// # Platform considerations /// /// This is the portable version of the function. It will always indicate that /// all files are owned by the root group. (Some systems call this 'wheel'.) pub fn get_unix_group(_metadata: &fs::Metadata, _path: &path::Path) -> io::Result<(u32, String)> { Ok((0, "root".to_string())) }