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
102unsafe impl Send for OsString {}
103
104impl Drop for OsString {
105 fn drop(&mut self) {
106 let ptr = self.alloc.as_ptr() as *mut libc::c_void;
107 unsafe { libc::free(ptr) }
108 }
109}
110
111impl AsRef<OsStr> for OsString {
112 fn as_ref(&self) -> &OsStr {
113 unsafe {
114 OsStr::from_slice(slice::from_raw_parts(
115 self.alloc.as_ptr(),
116 self.len,
117 ))
118 }
119 }
120}
121
122#[repr(transparent)]
124pub struct OsStr {
125 bytes: [libc::c_char],
126}
127
128impl OsStr {
129 unsafe fn from_slice(slice: &[libc::c_char]) -> &Self {
131 transmute(slice)
132 }
133}
134
135impl fmt::Debug for OsStr {
136 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
137 let mut first = false;
138 write!(fmt, "[")?;
139
140 for &signed in &self.bytes {
141 let byte = signed as u8;
142 if first {
143 first = false;
144 } else {
145 write!(fmt, ", ")?;
146 }
147 if byte.is_ascii() {
148 write!(fmt, "{:?}", char::from(byte))?;
149 } else {
150 write!(fmt, "'\\x{:x}'", byte)?;
151 }
152 }
153
154 write!(fmt, "]")?;
155 Ok(())
156 }
157}
158
159impl fmt::Display for OsStr {
160 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
161 let ptr = self.bytes.as_ptr();
162 let len = self.bytes.len();
163 let slice = unsafe { slice::from_raw_parts(ptr as _, len) };
164
165 let mut sub = slice;
166
167 while sub.len() > 0 {
168 match str::from_utf8(sub) {
169 Ok(string) => {
170 write!(fmt, "{}", string)?;
171 sub = &[];
172 },
173 Err(err) => {
174 let string = str::from_utf8(&sub[.. err.valid_up_to()])
175 .expect("Inconsistent utf8 error");
176 write!(fmt, "{}�", string,)?;
177
178 sub = &sub[err.valid_up_to() + 1 ..];
179 },
180 }
181 }
182
183 Ok(())
184 }
185}
186
187impl<'str> IntoOsString for &'str OsStr {
188 fn into_os_string(self) -> Result<OsString, Error> {
189 let len = self.bytes.len();
190 let alloc = unsafe { libc::malloc(len + 1) };
191 let alloc = match NonNull::new(alloc as *mut libc::c_char) {
192 Some(alloc) => alloc,
193 None => {
194 return Err(Error::last_os_error());
195 },
196 };
197 unsafe {
198 libc::memcpy(
199 alloc.as_ptr() as *mut libc::c_void,
200 self.bytes.as_ptr() as *const libc::c_void,
201 len + 1,
202 );
203 }
204
205 Ok(OsString { alloc, len })
206 }
207}
208
209impl ToOsStr for str {
210 fn to_os_str(&self) -> Result<EitherOsStr, Error> {
211 make_os_str(self.as_bytes())
212 }
213}
214
215#[cfg(feature = "std")]
216impl ToOsStr for ffi::OsStr {
217 fn to_os_str(&self) -> Result<EitherOsStr, Error> {
218 make_os_str(self.as_bytes())
219 }
220}
221
222fn make_os_str(slice: &[u8]) -> Result<EitherOsStr, Error> {
226 if let Some((&last, init)) = slice.split_last() {
227 if init.contains(&0) {
228 panic!("Path to file cannot contain nul-byte in the middle");
229 }
230 if last == 0 {
231 let str = unsafe { OsStr::from_slice(transmute(slice)) };
232 return Ok(EitherOsStr::Borrowed(str));
233 }
234 }
235
236 let alloc = unsafe { libc::malloc(slice.len() + 1) };
237 let alloc = match NonNull::new(alloc as *mut libc::c_char) {
238 Some(alloc) => alloc,
239 None => {
240 return Err(Error::last_os_error());
241 },
242 };
243 unsafe {
244 libc::memcpy(
245 alloc.as_ptr() as *mut libc::c_void,
246 slice.as_ptr() as *const libc::c_void,
247 slice.len(),
248 );
249 *alloc.as_ptr().add(slice.len()) = 0;
250 }
251
252 Ok(EitherOsStr::Owned(OsString { alloc, len: slice.len() }))
253}
254
255pub fn pid() -> Pid {
257 unsafe { libc::getpid() }
258}
259
260pub fn open(path: &OsStr) -> Result<FileDesc, Error> {
265 let fd = unsafe {
266 libc::open(
267 path.bytes.as_ptr(),
268 libc::O_RDWR | libc::O_CLOEXEC | libc::O_CREAT,
269 (libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH)
270 as libc::c_int,
271 )
272 };
273
274 if fd >= 0 {
275 Ok(fd)
276 } else {
277 Err(Error::last_os_error())
278 }
279}
280
281pub fn write(fd: FileDesc, mut bytes: &[u8]) -> Result<(), Error> {
283 while bytes.len() > 0 {
284 let written = unsafe {
285 libc::write(fd, bytes.as_ptr() as *const libc::c_void, bytes.len())
286 };
287 if written < 0 && errno() != libc::EAGAIN {
288 return Err(Error::last_os_error());
289 }
290 bytes = &bytes[written as usize ..];
291 }
292
293 Ok(())
294}
295
296pub fn fsync(fd: FileDesc) -> Result<(), Error> {
297 let result = unsafe { libc::fsync(fd) };
298
299 if result >= 0 {
300 Ok(())
301 } else {
302 Err(Error::last_os_error())
303 }
304}
305
306pub fn truncate(fd: FileDesc) -> Result<(), Error> {
309 let res = unsafe { libc::lseek(fd, 0, libc::SEEK_SET) };
310 if res < 0 {
311 return Err(Error::last_os_error());
312 }
313
314 let res = unsafe { libc::ftruncate(fd, 0) };
315 if res < 0 {
316 Err(Error::last_os_error())
317 } else {
318 Ok(())
319 }
320}
321
322pub fn lock(fd: FileDesc) -> Result<(), Error> {
324 let res = unsafe { libc::flock(fd, libc::LOCK_EX) };
325 if res >= 0 {
326 Ok(())
327 } else {
328 Err(Error::last_os_error())
329 }
330}
331
332pub fn try_lock(fd: FileDesc) -> Result<bool, Error> {
334 let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
335 if res >= 0 {
336 Ok(true)
337 } else {
338 let err = errno();
339 if err == libc::EWOULDBLOCK || err == libc::EINTR {
340 Ok(false)
341 } else {
342 Err(Error::from_raw_os_error(err as i32))
343 }
344 }
345}
346
347pub fn unlock(fd: FileDesc) -> Result<(), Error> {
349 let res = unsafe { libc::flock(fd, libc::LOCK_UN) };
350 if res >= 0 {
351 Ok(())
352 } else {
353 Err(Error::last_os_error())
354 }
355}
356
357pub fn close(fd: FileDesc) {
359 unsafe { libc::close(fd) };
360}