1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Higher-level interfaces to libc::* functions and operating system services.
13 //! In general these take and return rust types, use rust idioms (enums, closures, vectors) rather
14 //! than C idioms, and do more extensive safety checks.
16 //! This module is not meant to only contain 1:1 mappings to libc entries; any os-interface code
17 //! that is reasonably useful and broadly applicable can go here. Including utility routines that
18 //! merely build on other os code.
20 //! We assume the general case is that users do not care, and do not want to be made to care, which
21 //! operating system they are on. While they may want to special case various special cases -- and
22 //! so we will not _hide_ the facts of which OS the user is on -- they should be given the
23 //! opportunity to write OS-ignorant code by default.
27 #![allow(missing_docs)]
28 #![allow(non_snake_case)]
29 #![allow(unused_imports)]
31 use self::MemoryMapKind::*;
32 use self::MapOption::*;
33 use self::MapError::*;
36 use error::{FromError, Error};
38 use io::{IoResult, IoError};
39 use iter::{Iterator, IteratorExt};
41 use libc::{c_void, c_int, c_char};
44 use ops::{Drop, FnOnce};
46 use option::Option::{Some, None};
47 use path::{Path, GenericPath, BytesContainer};
49 use sys::os as os_imp;
53 use result::Result::{Err, Ok};
54 use slice::{AsSlice, SliceExt};
55 use str::{Str, StrExt};
56 use string::{String, ToString};
57 use sync::atomic::{AtomicInt, ATOMIC_INT_INIT, Ordering};
60 #[cfg(unix)] use ffi::{self, CString};
62 #[cfg(unix)] pub use sys::ext as unix;
63 #[cfg(windows)] pub use sys::ext as windows;
65 /// Get the number of cores available
66 pub fn num_cpus() -> uint {
68 return rust_get_num_cpus() as uint;
72 fn rust_get_num_cpus() -> libc::uintptr_t;
76 pub const TMPBUF_SZ : uint = 1000u;
78 /// Returns the current working directory as a `Path`.
82 /// Returns an `Err` if the current working directory value is invalid.
85 /// * Current directory does not exist.
86 /// * There are insufficient permissions to access the current directory.
87 /// * The internal buffer is not large enough to hold the path.
94 /// // We assume that we are in a valid directory.
95 /// let current_working_directory = os::getcwd().unwrap();
96 /// println!("The current directory is {}", current_working_directory.display());
98 pub fn getcwd() -> IoResult<Path> {
103 Accessing environment variables is not generally threadsafe.
104 Serialize access through a global lock.
106 fn with_env_lock<T, F>(f: F) -> T where
109 use sync::{StaticMutex, MUTEX_INIT};
111 static LOCK: StaticMutex = MUTEX_INIT;
113 let _guard = LOCK.lock();
117 /// Returns a vector of (variable, value) pairs, for all the environment
118 /// variables of the current process.
120 /// Invalid UTF-8 bytes are replaced with \uFFFD. See `String::from_utf8_lossy()`
128 /// // We will iterate through the references to the element returned by os::env();
129 /// for &(ref key, ref value) in os::env().iter() {
130 /// println!("'{}': '{}'", key, value );
133 pub fn env() -> Vec<(String,String)> {
134 env_as_bytes().into_iter().map(|(k,v)| {
135 let k = String::from_utf8_lossy(k.as_slice()).into_owned();
136 let v = String::from_utf8_lossy(v.as_slice()).into_owned();
141 /// Returns a vector of (variable, value) byte-vector pairs for all the
142 /// environment variables of the current process.
143 pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> {
145 fn env_convert(input: Vec<Vec<u8>>) -> Vec<(Vec<u8>, Vec<u8>)> {
146 let mut pairs = Vec::new();
147 for p in input.iter() {
148 let mut it = p.splitn(1, |b| *b == b'=');
149 let key = it.next().unwrap().to_vec();
150 let default: &[u8] = &[];
151 let val = it.next().unwrap_or(default).to_vec();
152 pairs.push((key, val));
157 let unparsed_environ = sys::os::get_env_pairs();
158 env_convert(unparsed_environ)
164 /// Fetches the environment variable `n` from the current process, returning
165 /// None if the variable isn't set.
167 /// Any invalid UTF-8 bytes in the value are replaced by \uFFFD. See
168 /// `String::from_utf8_lossy()` for details.
172 /// Panics if `n` has any interior NULs.
179 /// let key = "HOME";
180 /// match os::getenv(key) {
181 /// Some(val) => println!("{}: {}", key, val),
182 /// None => println!("{} is not defined in the environment.", key)
185 pub fn getenv(n: &str) -> Option<String> {
186 getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_owned())
190 /// Fetches the environment variable `n` byte vector from the current process,
191 /// returning None if the variable isn't set.
195 /// Panics if `n` has any interior NULs.
196 pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
199 let s = CString::from_slice(n.as_bytes());
200 let s = libc::getenv(s.as_ptr()) as *const _;
204 Some(ffi::c_str_to_bytes(&s).to_vec())
211 /// Fetches the environment variable `n` from the current process, returning
212 /// None if the variable isn't set.
213 pub fn getenv(n: &str) -> Option<String> {
216 use sys::os::fill_utf16_buf_and_decode;
217 let mut n: Vec<u16> = n.utf16_units().collect();
219 fill_utf16_buf_and_decode(|buf, sz| {
220 libc::GetEnvironmentVariableW(n.as_ptr(), buf, sz)
227 /// Fetches the environment variable `n` byte vector from the current process,
228 /// returning None if the variable isn't set.
229 pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
230 getenv(n).map(|s| s.into_bytes())
233 /// Sets the environment variable `n` to the value `v` for the currently running
242 /// os::setenv(key, "VALUE");
243 /// match os::getenv(key) {
244 /// Some(ref val) => println!("{}: {}", key, val),
245 /// None => println!("{} is not defined in the environment.", key)
248 pub fn setenv<T: BytesContainer>(n: &str, v: T) {
250 fn _setenv(n: &str, v: &[u8]) {
253 let k = CString::from_slice(n.as_bytes());
254 let v = CString::from_slice(v);
255 if libc::funcs::posix01::unistd::setenv(k.as_ptr(),
256 v.as_ptr(), 1) != 0 {
257 panic!(IoError::last_error());
264 fn _setenv(n: &str, v: &[u8]) {
265 let mut n: Vec<u16> = n.utf16_units().collect();
267 let mut v: Vec<u16> = ::str::from_utf8(v).unwrap().utf16_units().collect();
272 if libc::SetEnvironmentVariableW(n.as_ptr(), v.as_ptr()) == 0 {
273 panic!(IoError::last_error());
279 _setenv(n, v.container_as_bytes())
282 /// Remove a variable from the environment entirely.
283 pub fn unsetenv(n: &str) {
285 fn _unsetenv(n: &str) {
288 let nbuf = CString::from_slice(n.as_bytes());
289 if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 {
290 panic!(IoError::last_error());
297 fn _unsetenv(n: &str) {
298 let mut n: Vec<u16> = n.utf16_units().collect();
302 if libc::SetEnvironmentVariableW(n.as_ptr(), ptr::null()) == 0 {
303 panic!(IoError::last_error());
312 /// Parses input according to platform conventions for the `PATH`
313 /// environment variable.
319 /// let key = "PATH";
320 /// match os::getenv_as_bytes(key) {
322 /// for path in os::split_paths(paths).iter() {
323 /// println!("'{}'", path.display());
326 /// None => println!("{} is not defined in the environment.", key)
329 pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
330 sys::os::split_paths(unparsed.container_as_bytes())
333 /// Joins a collection of `Path`s appropriately for the `PATH`
334 /// environment variable.
336 /// Returns a `Vec<u8>` on success, since `Path`s are not utf-8
337 /// encoded on all platforms.
339 /// Returns an `Err` (containing an error message) if one of the input
340 /// `Path`s contains an invalid character for constructing the `PATH`
341 /// variable (a double quote on Windows or a colon on Unix).
347 /// use std::path::Path;
349 /// let key = "PATH";
350 /// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
351 /// paths.push(Path::new("/home/xyz/bin"));
352 /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap());
354 pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
355 sys::os::join_paths(paths)
358 /// A low-level OS in-memory pipe.
361 /// A file descriptor representing the reading end of the pipe. Data written
362 /// on the `out` file descriptor can be read from this file descriptor.
364 /// A file descriptor representing the write end of the pipe. Data written
365 /// to this file descriptor can be read from the `input` file descriptor.
369 /// Creates a new low-level OS in-memory pipe.
371 /// This function can fail to succeed if there are no more resources available
372 /// to allocate a pipe.
374 /// This function is also unsafe as there is no destructor associated with the
375 /// `Pipe` structure will return. If it is not arranged for the returned file
376 /// descriptors to be closed, the file descriptors will leak. For safe handling
377 /// of this scenario, use `std::io::PipeStream` instead.
378 pub unsafe fn pipe() -> IoResult<Pipe> {
379 let (reader, writer) = try!(sys::os::pipe());
381 reader: reader.unwrap(),
382 writer: writer.unwrap(),
386 /// Returns the proper dll filename for the given basename of a file
388 #[cfg(not(target_os="ios"))]
389 pub fn dll_filename(base: &str) -> String {
390 format!("{}{}{}", consts::DLL_PREFIX, base, consts::DLL_SUFFIX)
393 /// Optionally returns the filesystem path to the current executable which is
394 /// running but with the executable name.
401 /// match os::self_exe_name() {
402 /// Some(exe_path) => println!("Path of this executable is: {}", exe_path.display()),
403 /// None => println!("Unable to get the path of this executable!")
406 pub fn self_exe_name() -> Option<Path> {
407 sys::os::load_self().and_then(Path::new_opt)
410 /// Optionally returns the filesystem path to the current executable which is
413 /// Like self_exe_name() but without the binary's name.
420 /// match os::self_exe_path() {
421 /// Some(exe_path) => println!("Executable's Path is: {}", exe_path.display()),
422 /// None => println!("Impossible to fetch the path of this executable.")
425 pub fn self_exe_path() -> Option<Path> {
426 self_exe_name().map(|mut p| { p.pop(); p })
429 /// Optionally returns the path to the current user's home directory if known.
433 /// Returns the value of the 'HOME' environment variable if it is set
434 /// and not equal to the empty string.
438 /// Returns the value of the 'HOME' environment variable if it is
439 /// set and not equal to the empty string. Otherwise, returns the value of the
440 /// 'USERPROFILE' environment variable if it is set and not equal to the empty
448 /// match os::homedir() {
449 /// Some(ref p) => println!("{}", p.display()),
450 /// None => println!("Impossible to get your home dir!")
453 pub fn homedir() -> Option<Path> {
456 fn _homedir() -> Option<Path> {
462 fn _homedir() -> Option<Path> {
463 aux_homedir("HOME").or(aux_homedir("USERPROFILE"))
467 fn aux_homedir(home_name: &str) -> Option<Path> {
468 match getenv_as_bytes(home_name) {
470 if p.is_empty() { None } else { Path::new_opt(p) }
478 /// Returns the path to a temporary directory.
480 /// On Unix, returns the value of the 'TMPDIR' environment variable if it is
481 /// set, otherwise for non-Android it returns '/tmp'. If Android, since there
482 /// is no global temporary folder (it is usually allocated per-app), we return
483 /// '/data/local/tmp'.
485 /// On Windows, returns the value of, in order, the 'TMP', 'TEMP',
486 /// 'USERPROFILE' environment variable if any are set and not the empty
487 /// string. Otherwise, tmpdir returns the path to the Windows directory.
488 pub fn tmpdir() -> Path {
491 fn getenv_nonempty(v: &str) -> Option<Path> {
504 fn lookup() -> Path {
505 let default = if cfg!(target_os = "android") {
506 Path::new("/data/local/tmp")
511 getenv_nonempty("TMPDIR").unwrap_or(default)
515 fn lookup() -> Path {
516 getenv_nonempty("TMP").or(
517 getenv_nonempty("TEMP").or(
518 getenv_nonempty("USERPROFILE").or(
519 getenv_nonempty("WINDIR")))).unwrap_or(Path::new("C:\\Windows"))
523 /// Convert a relative path to an absolute path
525 /// If the given path is relative, return it prepended with the current working
526 /// directory. If the given path is already an absolute path, return it
532 /// use std::path::Path;
534 /// // Assume we're in a path like /home/someuser
535 /// let rel_path = Path::new("..");
536 /// let abs_path = os::make_absolute(&rel_path).unwrap();
537 /// println!("The absolute path is {}", abs_path.display());
538 /// // Prints "The absolute path is /home"
540 // NB: this is here rather than in path because it is a form of environment
541 // querying; what it does depends on the process working directory, not just
543 pub fn make_absolute(p: &Path) -> IoResult<Path> {
547 getcwd().map(|mut cwd| {
554 /// Changes the current working directory to the specified path, returning
555 /// whether the change was completed successfully or not.
560 /// use std::path::Path;
562 /// let root = Path::new("/");
563 /// assert!(os::change_dir(&root).is_ok());
564 /// println!("Successfully changed working directory to {}!", root.display());
566 pub fn change_dir(p: &Path) -> IoResult<()> {
567 return sys::os::chdir(p);
570 /// Returns the platform-specific value of errno
571 pub fn errno() -> uint {
572 sys::os::errno() as uint
575 /// Return the string corresponding to an `errno()` value of `errnum`.
581 /// // Same as println!("{}", last_os_error());
582 /// println!("{}", os::error_string(os::errno() as uint));
584 pub fn error_string(errnum: uint) -> String {
585 return sys::os::error_string(errnum as i32);
588 /// Get a string representing the platform-dependent last error
589 pub fn last_os_error() -> String {
590 error_string(errno() as uint)
593 static EXIT_STATUS: AtomicInt = ATOMIC_INT_INIT;
595 /// Sets the process exit code
597 /// Sets the exit code returned by the process if all supervised tasks
598 /// terminate successfully (without panicking). If the current root task panics
599 /// and is supervised by the scheduler then any user-specified exit status is
600 /// ignored and the process exits with the default panic status.
602 /// Note that this is not synchronized against modifications of other threads.
603 pub fn set_exit_status(code: int) {
604 EXIT_STATUS.store(code, Ordering::SeqCst)
607 /// Fetches the process's current exit code. This defaults to 0 and can change
608 /// by calling `set_exit_status`.
609 pub fn get_exit_status() -> int {
610 EXIT_STATUS.load(Ordering::SeqCst)
613 #[cfg(target_os = "macos")]
614 unsafe fn load_argc_and_argv(argc: int,
615 argv: *const *const c_char) -> Vec<Vec<u8>> {
618 range(0, argc as uint).map(|i| {
619 ffi::c_str_to_bytes(&*argv.offset(i as int)).to_vec()
623 /// Returns the command line arguments
625 /// Returns a list of the command line arguments.
626 #[cfg(target_os = "macos")]
627 fn real_args_as_bytes() -> Vec<Vec<u8>> {
629 let (argc, argv) = (*_NSGetArgc() as int,
630 *_NSGetArgv() as *const *const c_char);
631 load_argc_and_argv(argc, argv)
635 // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
636 // and use underscores in their names - they're most probably
637 // are considered private and therefore should be avoided
638 // Here is another way to get arguments using Objective C
641 // In general it looks like:
643 // let args = [[NSProcessInfo processInfo] arguments]
644 // for i in range(0, [args count])
645 // res.push([args objectAtIndex:i])
647 #[cfg(target_os = "ios")]
648 fn real_args_as_bytes() -> Vec<Vec<u8>> {
652 #[link(name = "objc")]
654 fn sel_registerName(name: *const libc::c_uchar) -> Sel;
655 fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
656 fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
659 #[link(name = "Foundation", kind = "framework")]
662 type Sel = *const libc::c_void;
663 type NsId = *const libc::c_void;
665 let mut res = Vec::new();
668 let processInfoSel = sel_registerName("processInfo\0".as_ptr());
669 let argumentsSel = sel_registerName("arguments\0".as_ptr());
670 let utf8Sel = sel_registerName("UTF8String\0".as_ptr());
671 let countSel = sel_registerName("count\0".as_ptr());
672 let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr());
674 let klass = objc_getClass("NSProcessInfo\0".as_ptr());
675 let info = objc_msgSend(klass, processInfoSel);
676 let args = objc_msgSend(info, argumentsSel);
678 let cnt: int = mem::transmute(objc_msgSend(args, countSel));
679 for i in range(0, cnt) {
680 let tmp = objc_msgSend(args, objectAtSel, i);
681 let utf_c_str: *const libc::c_char =
682 mem::transmute(objc_msgSend(tmp, utf8Sel));
683 let s = CString::new(utf_c_str, false);
684 res.push(s.as_bytes_no_nul().to_vec())
691 #[cfg(any(target_os = "linux",
692 target_os = "android",
693 target_os = "freebsd",
694 target_os = "dragonfly"))]
695 fn real_args_as_bytes() -> Vec<Vec<u8>> {
697 rt::args::clone().unwrap_or_else(|| vec![])
701 fn real_args() -> Vec<String> {
702 real_args_as_bytes().into_iter()
704 String::from_utf8_lossy(v.as_slice()).into_owned()
709 fn real_args() -> Vec<String> {
713 let mut nArgs: c_int = 0;
714 let lpArgCount: *mut c_int = &mut nArgs;
715 let lpCmdLine = unsafe { GetCommandLineW() };
716 let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
718 let args: Vec<_> = range(0, nArgs as uint).map(|i| unsafe {
719 // Determine the length of this argument.
720 let ptr = *szArgList.offset(i as int);
722 while *ptr.offset(len as int) != 0 { len += 1; }
724 // Push it onto the list.
725 let ptr = ptr as *const u16;
726 let buf = slice::from_raw_buf(&ptr, len);
727 let opt_s = String::from_utf16(sys::os::truncate_utf16_at_nul(buf));
728 opt_s.ok().expect("CommandLineToArgvW returned invalid UTF-16")
732 LocalFree(szArgList as *mut c_void);
739 fn real_args_as_bytes() -> Vec<Vec<u8>> {
740 real_args().into_iter().map(|s| s.into_bytes()).collect()
743 type LPCWSTR = *const u16;
746 #[link_name="kernel32"]
748 fn GetCommandLineW() -> LPCWSTR;
749 fn LocalFree(ptr: *mut c_void);
753 #[link_name="shell32"]
755 fn CommandLineToArgvW(lpCmdLine: LPCWSTR,
756 pNumArgs: *mut c_int) -> *mut *mut u16;
759 /// Returns the arguments which this program was started with (normally passed
760 /// via the command line).
762 /// The first element is traditionally the path to the executable, but it can be
763 /// set to arbitrary text, and it may not even exist, so this property should not
764 /// be relied upon for security purposes.
766 /// The arguments are interpreted as utf-8, with invalid bytes replaced with \uFFFD.
767 /// See `String::from_utf8_lossy` for details.
773 /// // Prints each argument on a separate line
774 /// for argument in os::args().iter() {
775 /// println!("{}", argument);
778 pub fn args() -> Vec<String> {
782 /// Returns the arguments which this program was started with (normally passed
783 /// via the command line) as byte vectors.
784 pub fn args_as_bytes() -> Vec<Vec<u8>> {
788 #[cfg(target_os = "macos")]
790 // These functions are in crt_externs.h.
791 pub fn _NSGetArgc() -> *mut c_int;
792 pub fn _NSGetArgv() -> *mut *mut *mut c_char;
795 /// Returns the page size of the current architecture in bytes.
796 pub fn page_size() -> uint {
800 /// A memory mapped file or chunk of memory. This is a very system-specific
801 /// interface to the OS's memory mapping facilities (`mmap` on POSIX,
802 /// `VirtualAlloc`/`CreateFileMapping` on Windows). It makes no attempt at
803 /// abstracting platform differences, besides in error values returned. Consider
806 /// The memory map is released (unmapped) when the destructor is run, so don't
807 /// let it leave scope by accident if you want it to stick around.
808 #[allow(missing_copy_implementations)]
809 pub struct MemoryMap {
815 /// Type of memory map
816 pub enum MemoryMapKind {
817 /// Virtual memory map. Usually used to change the permissions of a given
818 /// chunk of memory. Corresponds to `VirtualAlloc` on Windows.
820 /// Virtual memory map. Usually used to change the permissions of a given
821 /// chunk of memory, or for allocation. Corresponds to `VirtualAlloc` on
826 impl Copy for MemoryMapKind {}
828 /// Options the memory map is created with
830 /// The memory should be readable
832 /// The memory should be writable
834 /// The memory should be executable
836 /// Create a map for a specific address range. Corresponds to `MAP_FIXED` on
839 /// Create a memory mapping for a file with a given HANDLE.
842 /// Create a memory mapping for a file with a given fd.
845 /// When using `MapFd`, the start of the map is `uint` bytes from the start
848 /// On POSIX, this can be used to specify the default flags passed to
849 /// `mmap`. By default it uses `MAP_PRIVATE` and, if not using `MapFd`,
850 /// `MAP_ANON`. This will override both of those. This is platform-specific
851 /// (the exact values used) and ignored on Windows.
852 MapNonStandardFlags(c_int),
855 impl Copy for MapOption {}
857 /// Possible errors when creating a map.
860 /// # The following are POSIX-specific
862 /// fd was not open for reading or, if using `MapWritable`, was not open for
867 /// Either the address given by `MapAddr` or offset given by `MapOffset` was
868 /// not a multiple of `MemoryMap::granularity` (unaligned to page size).
870 /// With `MapFd`, the fd does not support mapping.
872 /// If using `MapAddr`, the address + `min_len` was outside of the process's
873 /// address space. If using `MapFd`, the target of the fd didn't have enough
874 /// resources to fulfill the request.
876 /// A zero-length map was requested. This is invalid according to
877 /// [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html).
878 /// Not all platforms obey this, but this wrapper does.
880 /// Unrecognized error. The inner value is the unrecognized errno.
882 /// # The following are Windows-specific
884 /// Unsupported combination of protection flags
885 /// (`MapReadable`/`MapWritable`/`MapExecutable`).
887 /// When using `MapFd`, `MapOffset` was given (Windows does not support this
890 /// When using `MapFd`, there was already a mapping to the file.
892 /// Unrecognized error from `VirtualAlloc`. The inner value is the return
893 /// value of GetLastError.
894 ErrVirtualAlloc(uint),
895 /// Unrecognized error from `CreateFileMapping`. The inner value is the
896 /// return value of `GetLastError`.
897 ErrCreateFileMappingW(uint),
898 /// Unrecognized error from `MapViewOfFile`. The inner value is the return
899 /// value of `GetLastError`.
900 ErrMapViewOfFile(uint)
903 impl fmt::Show for MapError {
904 fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
905 let str = match *self {
906 ErrFdNotAvail => "fd not available for reading or writing",
907 ErrInvalidFd => "Invalid fd",
909 "Unaligned address, invalid flags, negative length or \
912 ErrNoMapSupport=> "File doesn't support mapping",
913 ErrNoMem => "Invalid address, or not enough available memory",
914 ErrUnsupProt => "Protection mode unsupported",
915 ErrUnsupOffset => "Offset in virtual memory mode is unsupported",
916 ErrAlreadyExists => "File mapping for specified file already exists",
917 ErrZeroLength => "Zero-length mapping not allowed",
918 ErrUnknown(code) => {
919 return write!(out, "Unknown error = {}", code)
921 ErrVirtualAlloc(code) => {
922 return write!(out, "VirtualAlloc failure = {}", code)
924 ErrCreateFileMappingW(code) => {
925 return write!(out, "CreateFileMappingW failure = {}", code)
927 ErrMapViewOfFile(code) => {
928 return write!(out, "MapViewOfFile failure = {}", code)
931 write!(out, "{}", str)
935 impl Error for MapError {
936 fn description(&self) -> &str { "memory map error" }
937 fn detail(&self) -> Option<String> { Some(self.to_string()) }
940 impl FromError<MapError> for Box<Error> {
941 fn from_error(err: MapError) -> Box<Error> {
946 // Round up `from` to be divisible by `to`
947 fn round_up(from: uint, to: uint) -> uint {
948 let r = if from % to == 0 {
951 from + to - (from % to)
962 /// Create a new mapping with the given `options`, at least `min_len` bytes
963 /// long. `min_len` must be greater than zero; see the note on
965 pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
969 return Err(ErrZeroLength)
971 let mut addr: *const u8 = ptr::null();
973 let mut flags = libc::MAP_PRIVATE;
976 let mut custom_flags = false;
977 let len = round_up(min_len, page_size());
979 for &o in options.iter() {
981 MapReadable => { prot |= libc::PROT_READ; },
982 MapWritable => { prot |= libc::PROT_WRITE; },
983 MapExecutable => { prot |= libc::PROT_EXEC; },
985 flags |= libc::MAP_FIXED;
989 flags |= libc::MAP_FILE;
992 MapOffset(offset_) => { offset = offset_ as off_t; },
993 MapNonStandardFlags(f) => { custom_flags = true; flags = f },
996 if fd == -1 && !custom_flags { flags |= libc::MAP_ANON; }
999 libc::mmap(addr as *mut c_void, len as libc::size_t, prot, flags,
1002 if r == libc::MAP_FAILED {
1003 Err(match errno() as c_int {
1004 libc::EACCES => ErrFdNotAvail,
1005 libc::EBADF => ErrInvalidFd,
1006 libc::EINVAL => ErrUnaligned,
1007 libc::ENODEV => ErrNoMapSupport,
1008 libc::ENOMEM => ErrNoMem,
1009 code => ErrUnknown(code as int)
1018 MapFile(ptr::null())
1024 /// Granularity that the offset or address must be for `MapOffset` and
1025 /// `MapAddr` respectively.
1026 pub fn granularity() -> uint {
1032 impl Drop for MemoryMap {
1033 /// Unmap the mapping. Panics the task if `munmap` panics.
1034 fn drop(&mut self) {
1035 if self.len == 0 { /* workaround for dummy_stack */ return; }
1038 // `munmap` only panics due to logic errors
1039 libc::munmap(self.data as *mut c_void, self.len as libc::size_t);
1046 /// Create a new mapping with the given `options`, at least `min_len` bytes long.
1047 pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
1048 use libc::types::os::arch::extra::{LPVOID, DWORD, SIZE_T, HANDLE};
1050 let mut lpAddress: LPVOID = ptr::null_mut();
1051 let mut readable = false;
1052 let mut writable = false;
1053 let mut executable = false;
1054 let mut handle: HANDLE = libc::INVALID_HANDLE_VALUE;
1055 let mut offset: uint = 0;
1056 let len = round_up(min_len, page_size());
1058 for &o in options.iter() {
1060 MapReadable => { readable = true; },
1061 MapWritable => { writable = true; },
1062 MapExecutable => { executable = true; }
1063 MapAddr(addr_) => { lpAddress = addr_ as LPVOID; },
1064 MapFd(handle_) => { handle = handle_; },
1065 MapOffset(offset_) => { offset = offset_; },
1066 MapNonStandardFlags(..) => {}
1070 let flProtect = match (executable, readable, writable) {
1071 (false, false, false) if handle == libc::INVALID_HANDLE_VALUE => libc::PAGE_NOACCESS,
1072 (false, true, false) => libc::PAGE_READONLY,
1073 (false, true, true) => libc::PAGE_READWRITE,
1074 (true, false, false) if handle == libc::INVALID_HANDLE_VALUE => libc::PAGE_EXECUTE,
1075 (true, true, false) => libc::PAGE_EXECUTE_READ,
1076 (true, true, true) => libc::PAGE_EXECUTE_READWRITE,
1077 _ => return Err(ErrUnsupProt)
1080 if handle == libc::INVALID_HANDLE_VALUE {
1082 return Err(ErrUnsupOffset);
1085 libc::VirtualAlloc(lpAddress,
1087 libc::MEM_COMMIT | libc::MEM_RESERVE,
1091 0 => Err(ErrVirtualAlloc(errno())),
1099 let dwDesiredAccess = match (executable, readable, writable) {
1100 (false, true, false) => libc::FILE_MAP_READ,
1101 (false, true, true) => libc::FILE_MAP_WRITE,
1102 (true, true, false) => libc::FILE_MAP_READ | libc::FILE_MAP_EXECUTE,
1103 (true, true, true) => libc::FILE_MAP_WRITE | libc::FILE_MAP_EXECUTE,
1104 _ => return Err(ErrUnsupProt) // Actually, because of the check above,
1105 // we should never get here.
1109 let mapping = libc::CreateFileMappingW(hFile,
1115 if mapping == ptr::null_mut() {
1116 return Err(ErrCreateFileMappingW(errno()));
1118 if errno() as c_int == libc::ERROR_ALREADY_EXISTS {
1119 return Err(ErrAlreadyExists);
1121 let r = libc::MapViewOfFile(mapping,
1123 ((len as u64) >> 32) as DWORD,
1124 (offset & 0xffff_ffff) as DWORD,
1127 0 => Err(ErrMapViewOfFile(errno())),
1131 kind: MapFile(mapping as *const u8)
1138 /// Granularity of MapAddr() and MapOffset() parameter values.
1139 /// This may be greater than the value returned by page_size().
1140 pub fn granularity() -> uint {
1143 let mut info = mem::zeroed();
1144 libc::GetSystemInfo(&mut info);
1146 return info.dwAllocationGranularity as uint;
1152 impl Drop for MemoryMap {
1153 /// Unmap the mapping. Panics the task if any of `VirtualFree`,
1154 /// `UnmapViewOfFile`, or `CloseHandle` fail.
1155 fn drop(&mut self) {
1156 use libc::types::os::arch::extra::{LPCVOID, HANDLE};
1157 use libc::consts::os::extra::FALSE;
1158 if self.len == 0 { return }
1163 if libc::VirtualFree(self.data as *mut c_void, 0,
1164 libc::MEM_RELEASE) == 0 {
1165 println!("VirtualFree failed: {}", errno());
1168 MapFile(mapping) => {
1169 if libc::UnmapViewOfFile(self.data as LPCVOID) == FALSE {
1170 println!("UnmapViewOfFile failed: {}", errno());
1172 if libc::CloseHandle(mapping as HANDLE) == FALSE {
1173 println!("CloseHandle failed: {}", errno());
1182 /// Returns the pointer to the memory created or modified by this map.
1183 pub fn data(&self) -> *mut u8 { self.data }
1184 /// Returns the number of bytes this map applies to.
1185 pub fn len(&self) -> uint { self.len }
1186 /// Returns the type of mapping this represents.
1187 pub fn kind(&self) -> MemoryMapKind { self.kind }
1190 #[cfg(target_os = "linux")]
1192 pub use os::arch_consts::ARCH;
1194 pub const FAMILY: &'static str = "unix";
1196 /// A string describing the specific operating system in use: in this
1198 pub const SYSNAME: &'static str = "linux";
1200 /// Specifies the filename prefix used for shared libraries on this
1201 /// platform: in this case, `lib`.
1202 pub const DLL_PREFIX: &'static str = "lib";
1204 /// Specifies the filename suffix used for shared libraries on this
1205 /// platform: in this case, `.so`.
1206 pub const DLL_SUFFIX: &'static str = ".so";
1208 /// Specifies the file extension used for shared libraries on this
1209 /// platform that goes after the dot: in this case, `so`.
1210 pub const DLL_EXTENSION: &'static str = "so";
1212 /// Specifies the filename suffix used for executable binaries on this
1213 /// platform: in this case, the empty string.
1214 pub const EXE_SUFFIX: &'static str = "";
1216 /// Specifies the file extension, if any, used for executable binaries
1217 /// on this platform: in this case, the empty string.
1218 pub const EXE_EXTENSION: &'static str = "";
1221 #[cfg(target_os = "macos")]
1223 pub use os::arch_consts::ARCH;
1225 pub const FAMILY: &'static str = "unix";
1227 /// A string describing the specific operating system in use: in this
1229 pub const SYSNAME: &'static str = "macos";
1231 /// Specifies the filename prefix used for shared libraries on this
1232 /// platform: in this case, `lib`.
1233 pub const DLL_PREFIX: &'static str = "lib";
1235 /// Specifies the filename suffix used for shared libraries on this
1236 /// platform: in this case, `.dylib`.
1237 pub const DLL_SUFFIX: &'static str = ".dylib";
1239 /// Specifies the file extension used for shared libraries on this
1240 /// platform that goes after the dot: in this case, `dylib`.
1241 pub const DLL_EXTENSION: &'static str = "dylib";
1243 /// Specifies the filename suffix used for executable binaries on this
1244 /// platform: in this case, the empty string.
1245 pub const EXE_SUFFIX: &'static str = "";
1247 /// Specifies the file extension, if any, used for executable binaries
1248 /// on this platform: in this case, the empty string.
1249 pub const EXE_EXTENSION: &'static str = "";
1252 #[cfg(target_os = "ios")]
1254 pub use os::arch_consts::ARCH;
1256 pub const FAMILY: &'static str = "unix";
1258 /// A string describing the specific operating system in use: in this
1260 pub const SYSNAME: &'static str = "ios";
1262 /// Specifies the filename suffix used for executable binaries on this
1263 /// platform: in this case, the empty string.
1264 pub const EXE_SUFFIX: &'static str = "";
1266 /// Specifies the file extension, if any, used for executable binaries
1267 /// on this platform: in this case, the empty string.
1268 pub const EXE_EXTENSION: &'static str = "";
1271 #[cfg(target_os = "freebsd")]
1273 pub use os::arch_consts::ARCH;
1275 pub const FAMILY: &'static str = "unix";
1277 /// A string describing the specific operating system in use: in this
1278 /// case, `freebsd`.
1279 pub const SYSNAME: &'static str = "freebsd";
1281 /// Specifies the filename prefix used for shared libraries on this
1282 /// platform: in this case, `lib`.
1283 pub const DLL_PREFIX: &'static str = "lib";
1285 /// Specifies the filename suffix used for shared libraries on this
1286 /// platform: in this case, `.so`.
1287 pub const DLL_SUFFIX: &'static str = ".so";
1289 /// Specifies the file extension used for shared libraries on this
1290 /// platform that goes after the dot: in this case, `so`.
1291 pub const DLL_EXTENSION: &'static str = "so";
1293 /// Specifies the filename suffix used for executable binaries on this
1294 /// platform: in this case, the empty string.
1295 pub const EXE_SUFFIX: &'static str = "";
1297 /// Specifies the file extension, if any, used for executable binaries
1298 /// on this platform: in this case, the empty string.
1299 pub const EXE_EXTENSION: &'static str = "";
1302 #[cfg(target_os = "dragonfly")]
1304 pub use os::arch_consts::ARCH;
1306 pub const FAMILY: &'static str = "unix";
1308 /// A string describing the specific operating system in use: in this
1309 /// case, `dragonfly`.
1310 pub const SYSNAME: &'static str = "dragonfly";
1312 /// Specifies the filename prefix used for shared libraries on this
1313 /// platform: in this case, `lib`.
1314 pub const DLL_PREFIX: &'static str = "lib";
1316 /// Specifies the filename suffix used for shared libraries on this
1317 /// platform: in this case, `.so`.
1318 pub const DLL_SUFFIX: &'static str = ".so";
1320 /// Specifies the file extension used for shared libraries on this
1321 /// platform that goes after the dot: in this case, `so`.
1322 pub const DLL_EXTENSION: &'static str = "so";
1324 /// Specifies the filename suffix used for executable binaries on this
1325 /// platform: in this case, the empty string.
1326 pub const EXE_SUFFIX: &'static str = "";
1328 /// Specifies the file extension, if any, used for executable binaries
1329 /// on this platform: in this case, the empty string.
1330 pub const EXE_EXTENSION: &'static str = "";
1333 #[cfg(target_os = "android")]
1335 pub use os::arch_consts::ARCH;
1337 pub const FAMILY: &'static str = "unix";
1339 /// A string describing the specific operating system in use: in this
1340 /// case, `android`.
1341 pub const SYSNAME: &'static str = "android";
1343 /// Specifies the filename prefix used for shared libraries on this
1344 /// platform: in this case, `lib`.
1345 pub const DLL_PREFIX: &'static str = "lib";
1347 /// Specifies the filename suffix used for shared libraries on this
1348 /// platform: in this case, `.so`.
1349 pub const DLL_SUFFIX: &'static str = ".so";
1351 /// Specifies the file extension used for shared libraries on this
1352 /// platform that goes after the dot: in this case, `so`.
1353 pub const DLL_EXTENSION: &'static str = "so";
1355 /// Specifies the filename suffix used for executable binaries on this
1356 /// platform: in this case, the empty string.
1357 pub const EXE_SUFFIX: &'static str = "";
1359 /// Specifies the file extension, if any, used for executable binaries
1360 /// on this platform: in this case, the empty string.
1361 pub const EXE_EXTENSION: &'static str = "";
1364 #[cfg(target_os = "windows")]
1366 pub use os::arch_consts::ARCH;
1368 pub const FAMILY: &'static str = "windows";
1370 /// A string describing the specific operating system in use: in this
1371 /// case, `windows`.
1372 pub const SYSNAME: &'static str = "windows";
1374 /// Specifies the filename prefix used for shared libraries on this
1375 /// platform: in this case, the empty string.
1376 pub const DLL_PREFIX: &'static str = "";
1378 /// Specifies the filename suffix used for shared libraries on this
1379 /// platform: in this case, `.dll`.
1380 pub const DLL_SUFFIX: &'static str = ".dll";
1382 /// Specifies the file extension used for shared libraries on this
1383 /// platform that goes after the dot: in this case, `dll`.
1384 pub const DLL_EXTENSION: &'static str = "dll";
1386 /// Specifies the filename suffix used for executable binaries on this
1387 /// platform: in this case, `.exe`.
1388 pub const EXE_SUFFIX: &'static str = ".exe";
1390 /// Specifies the file extension, if any, used for executable binaries
1391 /// on this platform: in this case, `exe`.
1392 pub const EXE_EXTENSION: &'static str = "exe";
1395 #[cfg(target_arch = "x86")]
1397 pub const ARCH: &'static str = "x86";
1400 #[cfg(target_arch = "x86_64")]
1402 pub const ARCH: &'static str = "x86_64";
1405 #[cfg(target_arch = "arm")]
1407 pub const ARCH: &'static str = "arm";
1410 #[cfg(target_arch = "aarch64")]
1412 pub const ARCH: &'static str = "aarch64";
1415 #[cfg(target_arch = "mips")]
1417 pub const ARCH: &'static str = "mips";
1420 #[cfg(target_arch = "mipsel")]
1422 pub const ARCH: &'static str = "mipsel";
1430 use os::{env, getcwd, getenv, make_absolute};
1431 use os::{split_paths, join_paths, setenv, unsetenv};
1437 pub fn last_os_error() {
1438 debug!("{}", os::last_os_error());
1441 fn make_rand_name() -> String {
1442 let mut rng = rand::thread_rng();
1443 let n = format!("TEST{}", rng.gen_ascii_chars().take(10u)
1444 .collect::<String>());
1445 assert!(getenv(n.as_slice()).is_none());
1450 fn test_num_cpus() {
1451 assert!(os::num_cpus() > 0);
1456 let n = make_rand_name();
1457 setenv(n.as_slice(), "VALUE");
1458 assert_eq!(getenv(n.as_slice()), Some("VALUE".to_string()));
1462 fn test_unsetenv() {
1463 let n = make_rand_name();
1464 setenv(n.as_slice(), "VALUE");
1465 unsetenv(n.as_slice());
1466 assert_eq!(getenv(n.as_slice()), None);
1471 fn test_setenv_overwrite() {
1472 let n = make_rand_name();
1473 setenv(n.as_slice(), "1");
1474 setenv(n.as_slice(), "2");
1475 assert_eq!(getenv(n.as_slice()), Some("2".to_string()));
1476 setenv(n.as_slice(), "");
1477 assert_eq!(getenv(n.as_slice()), Some("".to_string()));
1480 // Windows GetEnvironmentVariable requires some extra work to make sure
1481 // the buffer the variable is copied into is the right size
1484 fn test_getenv_big() {
1485 let mut s = "".to_string();
1488 s.push_str("aaaaaaaaaa");
1491 let n = make_rand_name();
1492 setenv(n.as_slice(), s.as_slice());
1493 debug!("{}", s.clone());
1494 assert_eq!(getenv(n.as_slice()), Some(s));
1498 fn test_self_exe_name() {
1499 let path = os::self_exe_name();
1500 assert!(path.is_some());
1501 let path = path.unwrap();
1502 debug!("{}", path.display());
1504 // Hard to test this function
1505 assert!(path.is_absolute());
1509 fn test_self_exe_path() {
1510 let path = os::self_exe_path();
1511 assert!(path.is_some());
1512 let path = path.unwrap();
1513 debug!("{}", path.display());
1515 // Hard to test this function
1516 assert!(path.is_absolute());
1521 fn test_env_getenv() {
1523 assert!(e.len() > 0u);
1525 let (n, v) = (*p).clone();
1527 let v2 = getenv(n.as_slice());
1528 // MingW seems to set some funky environment variables like
1529 // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1530 // from env() but not visible from getenv().
1531 assert!(v2.is_none() || v2 == Some(v));
1536 fn test_env_set_get_huge() {
1537 let n = make_rand_name();
1538 let s = repeat("x").take(10000).collect::<String>();
1539 setenv(n.as_slice(), s.as_slice());
1540 assert_eq!(getenv(n.as_slice()), Some(s));
1541 unsetenv(n.as_slice());
1542 assert_eq!(getenv(n.as_slice()), None);
1546 fn test_env_setenv() {
1547 let n = make_rand_name();
1550 setenv(n.as_slice(), "VALUE");
1551 assert!(!e.contains(&(n.clone(), "VALUE".to_string())));
1554 assert!(e.contains(&(n, "VALUE".to_string())));
1559 assert!((!Path::new("test-path").is_absolute()));
1561 let cwd = getcwd().unwrap();
1562 debug!("Current working directory: {}", cwd.display());
1564 debug!("{}", make_absolute(&Path::new("test-path")).unwrap().display());
1565 debug!("{}", make_absolute(&Path::new("/usr/bin")).unwrap().display());
1571 let oldhome = getenv("HOME");
1573 setenv("HOME", "/home/MountainView");
1574 assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1577 assert!(os::homedir().is_none());
1579 for s in oldhome.iter() {
1580 setenv("HOME", s.as_slice());
1588 let oldhome = getenv("HOME");
1589 let olduserprofile = getenv("USERPROFILE");
1592 setenv("USERPROFILE", "");
1594 assert!(os::homedir().is_none());
1596 setenv("HOME", "/home/MountainView");
1597 assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1601 setenv("USERPROFILE", "/home/MountainView");
1602 assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1604 setenv("HOME", "/home/MountainView");
1605 setenv("USERPROFILE", "/home/PaloAlto");
1606 assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1608 for s in oldhome.iter() {
1609 setenv("HOME", s.as_slice());
1611 for s in olduserprofile.iter() {
1612 setenv("USERPROFILE", s.as_slice());
1617 fn memory_map_rw() {
1618 use result::Result::{Ok, Err};
1620 let chunk = match os::MemoryMap::new(16, &[
1621 os::MapOption::MapReadable,
1622 os::MapOption::MapWritable
1625 Err(msg) => panic!("{}", msg)
1627 assert!(chunk.len >= 16);
1631 assert!(*chunk.data == 0xBE);
1636 fn memory_map_file() {
1639 use io::fs::{File, unlink};
1640 use io::SeekStyle::SeekSet;
1641 use io::FileMode::Open;
1642 use io::FileAccess::ReadWrite;
1644 #[cfg(not(windows))]
1645 fn get_fd(file: &File) -> libc::c_int {
1646 use os::unix::AsRawFd;
1651 fn get_fd(file: &File) -> libc::HANDLE {
1652 use os::windows::AsRawHandle;
1653 file.as_raw_handle()
1656 let mut path = tmpdir();
1657 path.push("mmap_file.tmp");
1658 let size = MemoryMap::granularity() * 2;
1659 let mut file = File::open_mode(&path, Open, ReadWrite).unwrap();
1660 file.seek(size as i64, SeekSet).unwrap();
1661 file.write_u8(0).unwrap();
1663 let chunk = MemoryMap::new(size / 2, &[
1664 MapOption::MapReadable,
1665 MapOption::MapWritable,
1666 MapOption::MapFd(get_fd(&file)),
1667 MapOption::MapOffset(size / 2)
1669 assert!(chunk.len > 0);
1673 assert!(*chunk.data == 0xbe);
1677 unlink(&path).unwrap();
1682 fn split_paths_windows() {
1683 fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
1684 split_paths(unparsed) ==
1685 parsed.iter().map(|s| Path::new(*s)).collect::<Vec<_>>()
1688 assert!(check_parse("", &mut [""]));
1689 assert!(check_parse(r#""""#, &mut [""]));
1690 assert!(check_parse(";;", &mut ["", "", ""]));
1691 assert!(check_parse(r"c:\", &mut [r"c:\"]));
1692 assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
1693 assert!(check_parse(r"c:\;c:\Program Files\",
1694 &mut [r"c:\", r"c:\Program Files\"]));
1695 assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
1696 assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#,
1697 &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]));
1702 fn split_paths_unix() {
1703 fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
1704 split_paths(unparsed) ==
1705 parsed.iter().map(|s| Path::new(*s)).collect::<Vec<_>>()
1708 assert!(check_parse("", &mut [""]));
1709 assert!(check_parse("::", &mut ["", "", ""]));
1710 assert!(check_parse("/", &mut ["/"]));
1711 assert!(check_parse("/:", &mut ["/", ""]));
1712 assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
1717 fn join_paths_unix() {
1718 fn test_eq(input: &[&str], output: &str) -> bool {
1719 join_paths(input).unwrap() == output.as_bytes()
1722 assert!(test_eq(&[], ""));
1723 assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"],
1724 "/bin:/usr/bin:/usr/local/bin"));
1725 assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""],
1726 ":/bin:::/usr/bin:"));
1727 assert!(join_paths(&["/te:st"]).is_err());
1732 fn join_paths_windows() {
1733 fn test_eq(input: &[&str], output: &str) -> bool {
1734 join_paths(input).unwrap() == output.as_bytes()
1737 assert!(test_eq(&[], ""));
1738 assert!(test_eq(&[r"c:\windows", r"c:\"],
1739 r"c:\windows;c:\"));
1740 assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""],
1741 r";c:\windows;;;c:\;"));
1742 assert!(test_eq(&[r"c:\te;st", r"c:\"],
1743 r#""c:\te;st";c:\"#));
1744 assert!(join_paths(&[r#"c:\te"st"#]).is_err());
1747 // More recursive_mkdir tests are in extra::tempfile