Skip to content

Commit 99600ba

Browse files
committed
move OsStr helpers to a separate file
1 parent 01bc08a commit 99600ba

File tree

4 files changed

+250
-240
lines changed

4 files changed

+250
-240
lines changed

src/helpers.rs

Lines changed: 1 addition & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
use std::ffi::{OsStr, OsString};
2-
use std::path::{Path, PathBuf};
3-
use std::{iter, mem};
41
use std::convert::TryFrom;
5-
use std::borrow::Cow;
6-
7-
#[cfg(unix)]
8-
use std::os::unix::ffi::{OsStrExt, OsStringExt};
9-
#[cfg(windows)]
10-
use std::os::windows::ffi::{OsStrExt, OsStringExt};
2+
use std::mem;
113

124
use rustc::mir;
135
use rustc::ty::{
@@ -462,237 +454,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
462454
}
463455
}
464456
}
465-
466-
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
467-
/// the Unix APIs usually handle.
468-
fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, &'a OsStr>
469-
where
470-
'tcx: 'a,
471-
'mir: 'a,
472-
{
473-
#[cfg(unix)]
474-
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
475-
Ok(OsStr::from_bytes(bytes))
476-
}
477-
#[cfg(not(unix))]
478-
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
479-
let s = std::str::from_utf8(bytes)
480-
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?;
481-
Ok(OsStr::new(s))
482-
}
483-
484-
let this = self.eval_context_ref();
485-
let bytes = this.memory.read_c_str(scalar)?;
486-
bytes_to_os_str(bytes)
487-
}
488-
489-
/// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
490-
/// which is what the Windows APIs usually handle.
491-
fn read_os_str_from_wide_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString>
492-
where
493-
'tcx: 'a,
494-
'mir: 'a,
495-
{
496-
#[cfg(windows)]
497-
pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
498-
Ok(OsString::from_wide(&u16_vec[..]))
499-
}
500-
#[cfg(not(windows))]
501-
pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
502-
let s = String::from_utf16(&u16_vec[..])
503-
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?;
504-
Ok(s.into())
505-
}
506-
507-
let u16_vec = self.eval_context_ref().memory.read_wide_str(scalar)?;
508-
u16vec_to_osstring(u16_vec)
509-
}
510-
511-
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
512-
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
513-
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
514-
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
515-
/// string length returned does not include the null terminator.
516-
fn write_os_str_to_c_str(
517-
&mut self,
518-
os_str: &OsStr,
519-
scalar: Scalar<Tag>,
520-
size: u64,
521-
) -> InterpResult<'tcx, (bool, u64)> {
522-
#[cfg(unix)]
523-
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
524-
Ok(os_str.as_bytes())
525-
}
526-
#[cfg(not(unix))]
527-
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
528-
// On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
529-
// intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
530-
// valid.
531-
os_str
532-
.to_str()
533-
.map(|s| s.as_bytes())
534-
.ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
535-
}
536-
537-
let bytes = os_str_to_bytes(os_str)?;
538-
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
539-
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
540-
let string_length = u64::try_from(bytes.len()).unwrap();
541-
if size <= string_length {
542-
return Ok((false, string_length));
543-
}
544-
self.eval_context_mut()
545-
.memory
546-
.write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
547-
Ok((true, string_length))
548-
}
549-
550-
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
551-
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
552-
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
553-
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
554-
/// string length returned does not include the null terminator.
555-
fn write_os_str_to_wide_str(
556-
&mut self,
557-
os_str: &OsStr,
558-
scalar: Scalar<Tag>,
559-
size: u64,
560-
) -> InterpResult<'tcx, (bool, u64)> {
561-
#[cfg(windows)]
562-
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
563-
Ok(os_str.encode_wide().collect())
564-
}
565-
#[cfg(not(windows))]
566-
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
567-
// On non-Windows platforms the best we can do to transform Vec<u16> from/to OS strings is to do the
568-
// intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
569-
// valid.
570-
os_str
571-
.to_str()
572-
.map(|s| s.encode_utf16().collect())
573-
.ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
574-
}
575-
576-
let u16_vec = os_str_to_u16vec(os_str)?;
577-
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
578-
// 0x0000 terminator to memory would cause an out-of-bounds access.
579-
let string_length = u64::try_from(u16_vec.len()).unwrap();
580-
if size <= string_length {
581-
return Ok((false, string_length));
582-
}
583-
584-
// Store the UTF-16 string.
585-
self.eval_context_mut()
586-
.memory
587-
.write_u16s(scalar, u16_vec.into_iter().chain(iter::once(0x0000)))?;
588-
Ok((true, string_length))
589-
}
590-
591-
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
592-
fn alloc_os_str_as_c_str(
593-
&mut self,
594-
os_str: &OsStr,
595-
memkind: MemoryKind<MiriMemoryKind>,
596-
) -> Pointer<Tag> {
597-
let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
598-
let this = self.eval_context_mut();
599-
600-
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
601-
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
602-
assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
603-
arg_place.ptr.assert_ptr()
604-
}
605-
606-
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`.
607-
fn alloc_os_str_as_wide_str(
608-
&mut self,
609-
os_str: &OsStr,
610-
memkind: MemoryKind<MiriMemoryKind>,
611-
) -> Pointer<Tag> {
612-
let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
613-
let this = self.eval_context_mut();
614-
615-
let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
616-
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
617-
assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
618-
arg_place.ptr.assert_ptr()
619-
}
620-
621-
/// Read a null-terminated sequence of bytes, and perform path separator conversion if needed.
622-
fn read_path_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
623-
where
624-
'tcx: 'a,
625-
'mir: 'a,
626-
{
627-
let this = self.eval_context_ref();
628-
let os_str = this.read_os_str_from_c_str(scalar)?;
629-
630-
#[cfg(windows)]
631-
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
632-
// Windows-on-Windows, all fine.
633-
Cow::Borrowed(Path::new(os_str))
634-
} else {
635-
// Unix target, Windows host. Need to convert target '/' to host '\'.
636-
let converted = os_str
637-
.encode_wide()
638-
.map(|wchar| if wchar == '/' as u16 { '\\' as u16 } else { wchar })
639-
.collect::<Vec<_>>();
640-
Cow::Owned(PathBuf::from(OsString::from_wide(&converted)))
641-
});
642-
#[cfg(unix)]
643-
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
644-
// Windows target, Unix host. Need to convert target '\' to host '/'.
645-
let converted = os_str
646-
.as_bytes()
647-
.iter()
648-
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
649-
.collect::<Vec<_>>();
650-
Cow::Owned(PathBuf::from(OsString::from_vec(converted)))
651-
} else {
652-
// Unix-on-Unix, all is fine.
653-
Cow::Borrowed(Path::new(os_str))
654-
});
655-
}
656-
657-
/// Write a Path to the machine memory, adjusting path separators if needed.
658-
fn write_path_to_c_str(
659-
&mut self,
660-
path: &Path,
661-
scalar: Scalar<Tag>,
662-
size: u64,
663-
) -> InterpResult<'tcx, (bool, u64)> {
664-
let this = self.eval_context_mut();
665-
666-
#[cfg(windows)]
667-
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
668-
// Windows-on-Windows, all fine.
669-
Cow::Borrowed(path.as_os_str())
670-
} else {
671-
// Unix target, Windows host. Need to convert host '\\' to target '/'.
672-
let converted = path
673-
.as_os_str()
674-
.encode_wide()
675-
.map(|wchar| if wchar == '\\' as u16 { '/' as u16 } else { wchar })
676-
.collect::<Vec<_>>();
677-
Cow::Owned(OsString::from_wide(&converted))
678-
};
679-
#[cfg(unix)]
680-
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
681-
// Windows target, Unix host. Need to convert host '/' to target '\'.
682-
let converted = path
683-
.as_os_str()
684-
.as_bytes()
685-
.iter()
686-
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
687-
.collect::<Vec<_>>();
688-
Cow::Owned(OsString::from_vec(converted))
689-
} else {
690-
// Unix-on-Unix, all is fine.
691-
Cow::Borrowed(path.as_os_str())
692-
};
693-
694-
this.write_os_str_to_c_str(&os_str, scalar, size)
695-
}
696457
}
697458

698459
pub fn immty_from_int_checked<'tcx>(

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub use crate::shims::env::{EnvVars, EvalContextExt as EnvEvalContextExt};
3939
pub use crate::shims::foreign_items::EvalContextExt as ForeignItemsEvalContextExt;
4040
pub use crate::shims::fs::{DirHandler, EvalContextExt as FileEvalContextExt, FileHandler};
4141
pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
42+
pub use crate::shims::os_str::EvalContextExt as OsStrEvalContextExt;
4243
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as PanicEvalContextExt};
4344
pub use crate::shims::time::EvalContextExt as TimeEvalContextExt;
4445
pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};

src/shims/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod env;
33
pub mod foreign_items;
44
pub mod fs;
55
pub mod intrinsics;
6+
pub mod os_str;
67
pub mod panic;
78
pub mod time;
89
pub mod tls;

0 commit comments

Comments
 (0)