1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Various Windows fixes

Improve handling of unicode on Windows
Disable a few crates on Windows that abuse unix APIs too much

Signed-off-by: Peter Atashian <retep998@gmail.com>
This commit is contained in:
Peter Atashian 2015-07-19 20:25:48 -04:00
parent 1a2be4bf7f
commit 47f82f0de2
14 changed files with 153 additions and 139 deletions

View file

@ -52,7 +52,6 @@ PROGS := \
dirname \
echo \
env \
du \
expand \
factor \
false \
@ -62,7 +61,6 @@ PROGS := \
hashsum \
ln \
mkdir \
mv \
nl \
nproc \
od \
@ -85,13 +83,11 @@ PROGS := \
tac \
tee \
test \
touch \
tr \
true \
truncate \
tsort \
unexpand \
unlink \
uniq \
wc \
yes \
@ -101,6 +97,7 @@ PROGS := \
UNIX_PROGS := \
chroot \
du \
groups \
hostid \
hostname \
@ -108,12 +105,15 @@ UNIX_PROGS := \
kill \
logname \
mkfifo \
mv \
nice \
nohup \
stdbuf \
timeout \
touch \
tty \
uname \
unlink \
uptime \
users
@ -256,6 +256,10 @@ define DEP_BUILD
DEP_$(1):
ifeq ($(1),crypto)
cd $(BASEDIR)/deps && $(CARGO) build --package rust-crypto --release
else ifeq ($(1),kernel32)
cd $(BASEDIR)/deps && $(CARGO) build --package kernel32-sys --release
else ifeq ($(1),advapi32)
cd $(BASEDIR)/deps && $(CARGO) build --package advapi32-sys --release
else
cd $(BASEDIR)/deps && $(CARGO) build --package $(1) --release
endif

3
deps/Cargo.toml vendored
View file

@ -17,3 +17,6 @@ rust-crypto = "0.2.31"
rustc-serialize = "0.3.15"
time = "0.1.26"
unicode-width = "0.1.1"
winapi = "0.2"
advapi32-sys = "0.1"
kernel32-sys = "0.1"

View file

@ -124,17 +124,17 @@ fn verify_mode(modes: &str) -> Result<(), String> {
#[cfg(windows)]
#[inline]
// XXX: THIS IS NOT TESTED!!!
fn verify_mode(mode: &str) -> Result<(), String> {
fn verify_mode(modes: &str) -> Result<(), String> {
let re: regex::Regex = Regex::new(r"^[ugoa]*(?:[-+=](?:([rwxXst]*)|[ugo]))+|[-+=]?([0-7]+)$").unwrap();
for mode in modes.split(',') {
match re.captures(mode) {
Some(cap) => {
let symbols = cap.at(1);
let numbers = cap.at(2);
let symbols = cap.at(1).unwrap();
let numbers = cap.at(2).unwrap();
if symbols.contains("s") || symbols.contains("t") {
return Err("The 's' and 't' modes are not supported on Windows".to_string());
} else if numbers.len() >= 4 && numbers.slice_to(num_len - 3).find(|ch| ch != '0').is_some() {
return Err("Setuid, setgid, and sticky modes are not supported on Windows".to_string());
return Err("The 's' and 't' modes are not supported on Windows".into());
} else if numbers.len() >= 4 && numbers[..numbers.len() - 3].find(|ch| ch != '0').is_some() {
return Err("Setuid, setgid, and sticky modes are not supported on Windows".into());
}
}
None => return Err(format!("invalid mode '{}'", mode))
@ -191,6 +191,14 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
r
}
#[cfg(windows)]
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<libc::mode_t>, cmode: Option<&String>) -> Result<(), i32> {
// chmod is useless on Windows
// it doesn't set any permissions at all
// instead it just sets the readonly attribute on the file
Err(0)
}
#[cfg(unix)]
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<libc::mode_t>, cmode: Option<&String>) -> Result<(), i32> {
let path = CString::new(name).unwrap_or_else(|e| panic!("{}", e));
match fmode {

View file

@ -7,8 +7,6 @@
* file that was distributed with this source code.
*/
extern crate libc;
macro_rules! show_error(
($($args:tt)+) => ({
pipe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME);
@ -46,14 +44,14 @@ macro_rules! eprintln(
macro_rules! crash(
($exitcode:expr, $($args:tt)+) => ({
show_error!($($args)+);
unsafe { ::util::libc::exit($exitcode as ::util::libc::c_int); }
::std::process::exit($exitcode)
})
);
#[macro_export]
macro_rules! exit(
($exitcode:expr) => ({
unsafe { ::util::libc::exit($exitcode); }
::std::process::exit($exitcode)
})
);

35
src/common/wide.rs Normal file
View file

@ -0,0 +1,35 @@
/*
* This file is part of the uutils coreutils package.
*
* (c) Peter Atashian <retep998@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
pub trait ToWide {
fn to_wide(&self) -> Vec<u16>;
fn to_wide_null(&self) -> Vec<u16>;
}
impl<T> ToWide for T where T: AsRef<OsStr> {
fn to_wide(&self) -> Vec<u16> {
self.as_ref().encode_wide().collect()
}
fn to_wide_null(&self) -> Vec<u16> {
self.as_ref().encode_wide().chain(Some(0)).collect()
}
}
pub trait FromWide {
fn from_wide(wide: &[u16]) -> Self;
fn from_wide_null(wide: &[u16]) -> Self;
}
impl FromWide for String {
fn from_wide(wide: &[u16]) -> String {
OsString::from_wide(wide).to_string_lossy().into_owned()
}
fn from_wide_null(wide: &[u16]) -> String {
let len = wide.iter().take_while(|&&c| c != 0).count();
OsString::from_wide(&wide[..len]).to_string_lossy().into_owned()
}
}

View file

@ -1 +1 @@
DEPLIBS += time
DEPLIBS += time kernel32 winapi

View file

@ -25,7 +25,7 @@ macro_rules! silent_unwrap(
($exp:expr) => (
match $exp {
Ok(_) => (),
Err(_) => unsafe { ::util::libc::exit(1) }
Err(_) => ::std::process::exit(1),
}
)
);

View file

@ -1 +1 @@
DEPLIBS += regex regex-syntax crypto rand rustc-serialize time
DEPLIBS += regex regex-syntax crypto rand rustc-serialize time winapi kernel32

View file

@ -136,6 +136,8 @@ fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 {
show_info!("created directory '{}'", path.display());
}
#[cfg(unix)]
fn chmod(path: &Path, mode: u16) -> i32 {
let directory = CString::new(path.as_os_str().to_str().unwrap()).unwrap_or_else(|e| crash!(1, "{}", e));
let mode = mode as libc::mode_t;
@ -143,6 +145,12 @@ fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 {
show_info!("{}: errno {}", path.display(), Error::last_os_error().raw_os_error().unwrap());
return 1;
}
0
}
#[cfg(windows)]
fn chmod(path: &Path, mode: u16) -> i32 {
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
0
}
chmod(path, mode)
}

1
src/sync/deps.mk Normal file
View file

@ -0,0 +1 @@
DEPLIBS += winapi kernel32

View file

@ -35,103 +35,62 @@ mod platform {
#[cfg(windows)]
mod platform {
pub use super::libc;
use std::{mem, string};
use std::ptr::null;
extern crate winapi;
extern crate kernel32;
#[path = "../../common/wide.rs"] mod wide;
use std::{mem};
use std::fs::OpenOptions;
use std::io::{Write};
use std::os::windows::prelude::*;
use self::wide::{FromWide, ToWide};
extern "system" {
fn CreateFileA(lpFileName: *const libc::c_char,
dwDesiredAccess: libc::uint32_t,
dwShareMode: libc::uint32_t,
lpSecurityAttributes: *const libc::c_void, // *LPSECURITY_ATTRIBUTES
dwCreationDisposition: libc::uint32_t,
dwFlagsAndAttributes: libc::uint32_t,
hTemplateFile: *const libc::c_void) -> *const libc::c_void;
fn GetDriveTypeA(lpRootPathName: *const libc::c_char) -> libc::c_uint;
fn GetLastError() -> libc::uint32_t;
fn FindFirstVolumeA(lpszVolumeName: *mut libc::c_char,
cchBufferLength: libc::uint32_t) -> *const libc::c_void;
fn FindNextVolumeA(hFindVolume: *const libc::c_void,
lpszVolumeName: *mut libc::c_char,
cchBufferLength: libc::uint32_t) -> libc::c_int;
fn FindVolumeClose(hFindVolume: *const libc::c_void) -> libc::c_int;
fn FlushFileBuffers(hFile: *const libc::c_void) -> libc::c_int;
}
#[allow(unused_unsafe)]
unsafe fn flush_volume(name: &str) {
let name_buffer = name.to_c_str().as_ptr();
if 0x00000003 == GetDriveTypeA(name_buffer) { // DRIVE_FIXED
let name_wide = name.to_wide_null();
if kernel32::GetDriveTypeW(name_wide.as_ptr()) == winapi::DRIVE_FIXED {
let sliced_name = &name[..name.len() - 1]; // eliminate trailing backslash
let sliced_name_buffer = sliced_name.to_c_str().as_ptr();
match CreateFileA(sliced_name_buffer,
0xC0000000, // GENERIC_WRITE
0x00000003, // FILE_SHARE_WRITE,
null(),
0x00000003, // OPEN_EXISTING
0,
null()) {
-1 => { // INVALID_HANDLE_VALUE
crash!(GetLastError(), "failed to create volume handle");
}
handle => {
if FlushFileBuffers(handle) == 0 {
crash!(GetLastError(), "failed to flush file buffer");
}
}
}
}
}
#[allow(unused_unsafe)]
unsafe fn find_first_volume() -> (String, *const libc::c_void) {
let mut name: [libc::c_char; 260] = mem::uninitialized(); // MAX_PATH
match FindFirstVolumeA(name.as_mut_ptr(),
name.len() as libc::uint32_t) {
-1 => { // INVALID_HANDLE_VALUE
crash!(GetLastError(), "failed to find first volume");
}
handle => {
(string::raw::from_buf(name.as_ptr() as *const u8), handle)
match OpenOptions::new().write(true).open(sliced_name) {
Ok(file) => if kernel32::FlushFileBuffers(file.as_raw_handle()) == 0 {
crash!(kernel32::GetLastError() as i32, "failed to flush file buffer");
},
Err(e) => crash!(e.raw_os_error().unwrap_or(1), "failed to create volume handle")
}
}
}
#[allow(unused_unsafe)]
unsafe fn find_first_volume() -> (String, winapi::HANDLE) {
let mut name: [winapi::WCHAR; winapi::MAX_PATH] = mem::uninitialized();
let handle = kernel32::FindFirstVolumeW(name.as_mut_ptr(), name.len() as winapi::DWORD);
if handle == winapi::INVALID_HANDLE_VALUE {
crash!(kernel32::GetLastError() as i32, "failed to find first volume");
}
(String::from_wide_null(&name), handle)
}
unsafe fn find_all_volumes() -> Vec<String> {
match find_first_volume() {
(first_volume, next_volume_handle) => {
let (first_volume, next_volume_handle) = find_first_volume();
let mut volumes = vec![first_volume];
loop {
let mut name: [libc::c_char; 260] = mem::uninitialized(); // MAX_PATH
match FindNextVolumeA(next_volume_handle,
name.as_mut_ptr(),
name.len() as libc::uint32_t) {
0 => {
match GetLastError() {
0x12 => { // ERROR_NO_MORE_FILES
FindVolumeClose(next_volume_handle); // ignore FindVolumeClose() failures
break;
let mut name: [winapi::WCHAR; winapi::MAX_PATH] = mem::uninitialized();
if kernel32::FindNextVolumeW(
next_volume_handle, name.as_mut_ptr(), name.len() as winapi::DWORD
) == 0 {
match kernel32::GetLastError() {
winapi::ERROR_NO_MORE_FILES => {
kernel32::FindVolumeClose(next_volume_handle);
return volumes
},
err => crash!(err as i32, "failed to find next volume"),
}
err => {
crash!(err, "failed to find next volume");
}
}
}
_ => {
volumes.push(string::raw::from_buf(name.as_ptr() as *const u8));
}
}
}
volumes
} else {
volumes.push(String::from_wide_null(&name));
}
}
}
pub unsafe fn do_sync() -> int {
pub unsafe fn do_sync() -> isize {
let volumes = find_all_volumes();
for vol in volumes.iter() {
flush_volume(&vol);
for vol in &volumes {
flush_volume(vol);
}
0
}

View file

@ -20,8 +20,10 @@ use std::str::{from_utf8};
static NAME: &'static str = "test";
// TODO: decide how to handle non-UTF8 input for all the utils
// Definitely don't use [u8], try keeping it as OsStr or OsString instead
pub fn uumain(_: Vec<String>) -> i32 {
let args = args_os().collect::<Vec<OsString>>();
// This is completely disregarding valid windows paths that aren't valid unicode
let args = args.iter().map(|a| a.to_bytes().unwrap()).collect::<Vec<&[u8]>>();
if args.len() == 0 {
return 2;
@ -382,32 +384,26 @@ fn path(path: &[u8], cond: PathCondition) -> bool {
#[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool {
use std::old_io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
use std::old_io::fs::{stat};
use std::old_path::{Path};
let path = match Path::new_opt(path) {
Some(p) => p,
None => return false,
};
let stat = match stat(&path) {
use std::fs::metadata;
let path = from_utf8(path).unwrap();
let stat = match metadata(path) {
Ok(s) => s,
_ => return false,
};
match cond {
BlockSpecial => stat.kind == TypeBlockSpecial,
CharacterSpecial => false,
Directory => stat.kind == TypeDirectory,
Exists => true,
Regular => stat.kind == TypeFile,
GroupIDFlag => false,
SymLink => false,
FIFO => stat.kind == TypeNamedPipe,
Readable => false, // TODO
Socket => false, // TODO?
NonEmpty => stat.size > 0,
UserIDFlag => false,
Writable => false, // TODO
Executable => false, // TODO
PathCondition::BlockSpecial => false,
PathCondition::CharacterSpecial => false,
PathCondition::Directory => stat.is_dir(),
PathCondition::Exists => true,
PathCondition::Regular => stat.is_file(),
PathCondition::GroupIDFlag => false,
PathCondition::SymLink => false,
PathCondition::FIFO => false,
PathCondition::Readable => false, // TODO
PathCondition::Socket => false,
PathCondition::NonEmpty => stat.len() > 0,
PathCondition::UserIDFlag => false,
PathCondition::Writable => false, // TODO
PathCondition::Executable => false, // TODO
}
}

1
src/whoami/deps.mk Normal file
View file

@ -0,0 +1 @@
DEPLIBS += winapi advapi32 kernel32

View file

@ -7,21 +7,22 @@
* file that was distributed with this source code.
*/
use ::libc;
extern crate winapi;
extern crate advapi32;
#[path = "../../common/wide.rs"] #[macro_use] mod wide;
use std::mem;
use std::io::Write;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use self::wide::FromWide;
extern "system" {
pub fn GetUserNameA(out: *mut libc::c_char, len: *mut libc::uint32_t) -> libc::uint8_t;
}
#[allow(unused_unsafe)]
pub unsafe fn getusername() -> String {
// usernames can only be up to 104 characters in windows
let mut buffer: [libc::c_char; 105] = mem::uninitialized();
if !GetUserNameA(buffer.as_mut_ptr(), &mut (buffer.len() as libc::uint32_t)) == 0 {
crash!(1, "username is too long");
let mut buffer: [winapi::WCHAR; winapi::UNLEN as usize + 1] = mem::uninitialized();
let mut len = buffer.len() as winapi::DWORD;
if advapi32::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
crash!(1, "failed to get username");
}
String::from_utf8_lossy(::std::ffi::CStr::from_ptr(buffer.as_ptr()).to_bytes()).to_string()
String::from_wide(&buffer[..len as usize - 1])
}