fslock_arti_fork/
unix.rs

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    /// Yeah, I had to copy this from std
10    #[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
49/// A type representing file descriptor on Unix.
50pub type FileDesc = libc::c_int;
51
52/// A type representing Process ID on Unix.
53pub type Pid = libc::pid_t;
54
55#[cfg(feature = "std")]
56/// An IO error.
57pub type Error = std::io::Error;
58
59#[cfg(not(feature = "std"))]
60#[derive(Debug)]
61/// An IO error. Without std, you can only get a message or an OS error code.
62pub struct Error {
63    code: i32,
64}
65
66#[cfg(not(feature = "std"))]
67impl Error {
68    /// Creates an error from a raw OS error code.
69    pub fn from_raw_os_error(code: i32) -> Self {
70        Self { code }
71    }
72
73    /// Creates an error from the last OS error code.
74    pub fn last_os_error() -> Error {
75        Self::from_raw_os_error(errno() as i32)
76    }
77
78    /// Raw OS error code. Returns option for compatibility with std.
79    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
95/// Owned allocation of an OS-native string.
96pub struct OsString {
97    alloc: NonNull<libc::c_char>,
98    /// Length without the nul-byte.
99    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/// Borrowed allocation of an OS-native string.
123#[repr(transparent)]
124pub struct OsStr {
125    bytes: [libc::c_char],
126}
127
128impl OsStr {
129    /// Unsafe cause sequence needs to end with 0.
130    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
222/// Path must not contain a nul-byte in the middle, but a nul-byte in the end
223/// (and only in the end) is allowed, which in this case no extra allocation
224/// will be made. Otherwise, an extra allocation is made.
225fn 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
255/// Returns the ID of the current process.
256pub fn pid() -> Pid {
257    unsafe { libc::getpid() }
258}
259
260/// Opens a file with only purpose of locking it. Creates it if it does not
261/// exist. Path must not contain a nul-byte in the middle, but a nul-byte in the
262/// end (and only in the end) is allowed, which in this case no extra allocation
263/// will be made. Otherwise, an extra allocation is made.
264pub 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
281/// Writes data into the given open file.
282pub 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
306/// Truncates the file referenced by the given file descriptor and seeks it to
307/// the start.
308pub 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
322/// Tries to lock a file and blocks until it is possible to lock.
323pub 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
332/// Tries to lock a file but returns as soon as possible if already locked.
333pub 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
347/// Unlocks the file.
348pub 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
357/// Closes the file.
358pub fn close(fd: FileDesc) {
359    unsafe { libc::close(fd) };
360}