1use crate::{EitherOsStr, IntoOsString, ToOsStr};
2use core::{fmt, mem::transmute, ptr::NonNull, slice, str};
3
4#[cfg(feature = "std")]
5use std::{ffi, os::unix::ffi::OsStrExt};
6
7#[cfg(not(feature = "std"))]
8extern "C" {
9 #[cfg(not(target_os = "dragonfly"))]
11 #[cfg_attr(
12 any(
13 target_os = "linux",
14 target_os = "emscripten",
15 target_os = "fuchsia",
16 target_os = "l4re"
17 ),
18 link_name = "__errno_location"
19 )]
20 #[cfg_attr(
21 any(
22 target_os = "netbsd",
23 target_os = "openbsd",
24 target_os = "android",
25 target_os = "redox",
26 target_env = "newlib"
27 ),
28 link_name = "__errno"
29 )]
30 #[cfg_attr(target_os = "solaris", link_name = "___errno")]
31 #[cfg_attr(
32 any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
33 link_name = "__error"
34 )]
35 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
36 fn errno_location() -> *mut libc::c_int;
37}
38
39#[cfg(not(feature = "std"))]
40fn errno() -> libc::c_int {
41 unsafe { *errno_location() }
42}
43
44#[cfg(feature = "std")]
45fn errno() -> libc::c_int {
46 Error::last_os_error().raw_os_error().unwrap_or(0) as libc::c_int
47}
48
49pub type FileDesc = libc::c_int;
51
52pub type Pid = libc::pid_t;
54
55#[cfg(feature = "std")]
56pub type Error = std::io::Error;
58
59#[cfg(not(feature = "std"))]
60#[derive(Debug)]
61pub struct Error {
63 code: i32,
64}
65
66#[cfg(not(feature = "std"))]
67impl Error {
68 pub fn from_raw_os_error(code: i32) -> Self {
70 Self { code }
71 }
72
73 pub fn last_os_error() -> Error {
75 Self::from_raw_os_error(errno() as i32)
76 }
77
78 pub fn raw_os_error(&self) -> Option<i32> {
80 Some(self.code)
81 }
82}
83
84#[cfg(not(feature = "std"))]
85impl fmt::Display for Error {
86 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
87 let msg_ptr = unsafe { libc::strerror(self.code as libc::c_int) };
88 let len = unsafe { libc::strlen(msg_ptr) };
89 let slice = unsafe { slice::from_raw_parts(msg_ptr, len) };
90 write!(fmt, "{}", unsafe { OsStr::from_slice(slice) })?;
91 Ok(())
92 }
93}
94
95pub struct OsString {
97 alloc: NonNull<libc::c_char>,
98 len: usize,
100}
101
102impl Drop for OsString {
103 fn drop(&mut self) {
104 let ptr = self.alloc.as_ptr() as *mut libc::c_void;
105 unsafe { libc::free(ptr) }
106 }
107}
108
109impl AsRef<OsStr> for OsString {
110 fn as_ref(&self) -> &OsStr {
111 unsafe {
112 OsStr::from_slice(slice::from_raw_parts(
113 self.alloc.as_ptr(),
114 self.len,
115 ))
116 }
117 }
118}
119
120#[repr(transparent)]
122pub struct OsStr {
123 bytes: [libc::c_char],
124}
125
126impl OsStr {
127 unsafe fn from_slice(slice: &[libc::c_char]) -> &Self {
129 transmute(slice)
130 }
131}
132
133impl fmt::Debug for OsStr {
134 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
135 let mut first = false;
136 write!(fmt, "[")?;
137
138 for &signed in &self.bytes {
139 let byte = signed as u8;
140 if first {
141 first = false;
142 } else {
143 write!(fmt, ", ")?;
144 }
145 if byte.is_ascii() {
146 write!(fmt, "{:?}", char::from(byte))?;
147 } else {
148 write!(fmt, "'\\x{:x}'", byte)?;
149 }
150 }
151
152 write!(fmt, "]")?;
153 Ok(())
154 }
155}
156
157impl fmt::Display for OsStr {
158 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
159 let ptr = self.bytes.as_ptr();
160 let len = self.bytes.len();
161 let slice = unsafe { slice::from_raw_parts(ptr as _, len) };
162
163 let mut sub = slice;
164
165 while sub.len() > 0 {
166 match str::from_utf8(sub) {
167 Ok(string) => {
168 write!(fmt, "{}", string)?;
169 sub = &[];
170 },
171 Err(err) => {
172 let string = str::from_utf8(&sub[.. err.valid_up_to()])
173 .expect("Inconsistent utf8 error");
174 write!(fmt, "{}�", string,)?;
175
176 sub = &sub[err.valid_up_to() + 1 ..];
177 },
178 }
179 }
180
181 Ok(())
182 }
183}
184
185impl<'str> IntoOsString for &'str OsStr {
186 fn into_os_string(self) -> Result<OsString, Error> {
187 let len = self.bytes.len();
188 let alloc = unsafe { libc::malloc(len + 1) };
189 let alloc = match NonNull::new(alloc as *mut libc::c_char) {
190 Some(alloc) => alloc,
191 None => {
192 return Err(Error::last_os_error());
193 },
194 };
195 unsafe {
196 libc::memcpy(
197 alloc.as_ptr() as *mut libc::c_void,
198 self.bytes.as_ptr() as *const libc::c_void,
199 len + 1,
200 );
201 }
202
203 Ok(OsString { alloc, len })
204 }
205}
206
207impl ToOsStr for str {
208 fn to_os_str(&self) -> Result<EitherOsStr, Error> {
209 make_os_str(self.as_bytes())
210 }
211}
212
213#[cfg(feature = "std")]
214impl ToOsStr for ffi::OsStr {
215 fn to_os_str(&self) -> Result<EitherOsStr, Error> {
216 make_os_str(self.as_bytes())
217 }
218}
219
220fn make_os_str(slice: &[u8]) -> Result<EitherOsStr, Error> {
224 if let Some((&last, init)) = slice.split_last() {
225 if init.contains(&0) {
226 panic!("Path to file cannot contain nul-byte in the middle");
227 }
228 if last == 0 {
229 let str = unsafe { OsStr::from_slice(transmute(slice)) };
230 return Ok(EitherOsStr::Borrowed(str));
231 }
232 }
233
234 let alloc = unsafe { libc::malloc(slice.len() + 1) };
235 let alloc = match NonNull::new(alloc as *mut libc::c_char) {
236 Some(alloc) => alloc,
237 None => {
238 return Err(Error::last_os_error());
239 },
240 };
241 unsafe {
242 libc::memcpy(
243 alloc.as_ptr() as *mut libc::c_void,
244 slice.as_ptr() as *const libc::c_void,
245 slice.len(),
246 );
247 *alloc.as_ptr().add(slice.len()) = 0;
248 }
249
250 Ok(EitherOsStr::Owned(OsString { alloc, len: slice.len() }))
251}
252
253pub fn pid() -> Pid {
255 unsafe { libc::getpid() }
256}
257
258pub fn open(path: &OsStr) -> Result<FileDesc, Error> {
263 let fd = unsafe {
264 libc::open(
265 path.bytes.as_ptr(),
266 libc::O_RDWR | libc::O_CLOEXEC | libc::O_CREAT,
267 (libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH)
268 as libc::c_int,
269 )
270 };
271
272 if fd >= 0 {
273 Ok(fd)
274 } else {
275 Err(Error::last_os_error())
276 }
277}
278
279pub fn write(fd: FileDesc, mut bytes: &[u8]) -> Result<(), Error> {
281 while bytes.len() > 0 {
282 let written = unsafe {
283 libc::write(fd, bytes.as_ptr() as *const libc::c_void, bytes.len())
284 };
285 if written < 0 && errno() != libc::EAGAIN {
286 return Err(Error::last_os_error());
287 }
288 bytes = &bytes[written as usize ..];
289 }
290
291 Ok(())
292}
293
294pub fn fsync(fd: FileDesc) -> Result<(), Error> {
295 let result = unsafe { libc::fsync(fd) };
296
297 if result >= 0 {
298 Ok(())
299 } else {
300 Err(Error::last_os_error())
301 }
302}
303
304pub fn truncate(fd: FileDesc) -> Result<(), Error> {
307 let res = unsafe { libc::lseek(fd, 0, libc::SEEK_SET) };
308 if res < 0 {
309 return Err(Error::last_os_error());
310 }
311
312 let res = unsafe { libc::ftruncate(fd, 0) };
313 if res < 0 {
314 Err(Error::last_os_error())
315 } else {
316 Ok(())
317 }
318}
319
320pub fn lock(fd: FileDesc) -> Result<(), Error> {
322 let res = unsafe { libc::flock(fd, libc::LOCK_EX) };
323 if res >= 0 {
324 Ok(())
325 } else {
326 Err(Error::last_os_error())
327 }
328}
329
330pub fn try_lock(fd: FileDesc) -> Result<bool, Error> {
332 let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
333 if res >= 0 {
334 Ok(true)
335 } else {
336 let err = errno();
337 if err == libc::EWOULDBLOCK || err == libc::EINTR {
338 Ok(false)
339 } else {
340 Err(Error::from_raw_os_error(err as i32))
341 }
342 }
343}
344
345pub fn unlock(fd: FileDesc) -> Result<(), Error> {
347 let res = unsafe { libc::flock(fd, libc::LOCK_UN) };
348 if res >= 0 {
349 Ok(())
350 } else {
351 Err(Error::last_os_error())
352 }
353}
354
355pub fn close(fd: FileDesc) {
357 unsafe { libc::close(fd) };
358}