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

3
deps/Cargo.toml vendored
View file

@ -17,3 +17,6 @@ rust-crypto = "0.2.31"
rustc-serialize = "0.3.15" rustc-serialize = "0.3.15"
time = "0.1.26" time = "0.1.26"
unicode-width = "0.1.1" 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)] #[cfg(windows)]
#[inline] #[inline]
// XXX: THIS IS NOT TESTED!!! // 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(); let re: regex::Regex = Regex::new(r"^[ugoa]*(?:[-+=](?:([rwxXst]*)|[ugo]))+|[-+=]?([0-7]+)$").unwrap();
for mode in modes.split(',') { for mode in modes.split(',') {
match re.captures(mode) { match re.captures(mode) {
Some(cap) => { Some(cap) => {
let symbols = cap.at(1); let symbols = cap.at(1).unwrap();
let numbers = cap.at(2); let numbers = cap.at(2).unwrap();
if symbols.contains("s") || symbols.contains("t") { if symbols.contains("s") || symbols.contains("t") {
return Err("The 's' and 't' 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.slice_to(num_len - 3).find(|ch| ch != '0').is_some() { } 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".to_string()); return Err("Setuid, setgid, and sticky modes are not supported on Windows".into());
} }
} }
None => return Err(format!("invalid mode '{}'", mode)) None => return Err(format!("invalid mode '{}'", mode))
@ -191,6 +191,14 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
r 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> { 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)); let path = CString::new(name).unwrap_or_else(|e| panic!("{}", e));
match fmode { match fmode {

View file

@ -7,8 +7,6 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
extern crate libc;
macro_rules! show_error( macro_rules! show_error(
($($args:tt)+) => ({ ($($args:tt)+) => ({
pipe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME); pipe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME);
@ -46,14 +44,14 @@ macro_rules! eprintln(
macro_rules! crash( macro_rules! crash(
($exitcode:expr, $($args:tt)+) => ({ ($exitcode:expr, $($args:tt)+) => ({
show_error!($($args)+); show_error!($($args)+);
unsafe { ::util::libc::exit($exitcode as ::util::libc::c_int); } ::std::process::exit($exitcode)
}) })
); );
#[macro_export] #[macro_export]
macro_rules! exit( macro_rules! exit(
($exitcode:expr) => ({ ($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) => ( ($exp:expr) => (
match $exp { match $exp {
Ok(_) => (), 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,13 +136,21 @@ fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 {
show_info!("created directory '{}'", path.display()); show_info!("created directory '{}'", path.display());
} }
let directory = CString::new(path.as_os_str().to_str().unwrap()).unwrap_or_else(|e| crash!(1, "{}", e)); #[cfg(unix)]
let mode = mode as libc::mode_t; 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;
if unsafe { libc::chmod(directory.as_ptr(), mode) } != 0 { if unsafe { libc::chmod(directory.as_ptr(), mode) } != 0 {
show_info!("{}: errno {}", path.display(), Error::last_os_error().raw_os_error().unwrap()); show_info!("{}: errno {}", path.display(), Error::last_os_error().raw_os_error().unwrap());
return 1; return 1;
}
0
} }
#[cfg(windows)]
0 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)] #[cfg(windows)]
mod platform { mod platform {
pub use super::libc; extern crate winapi;
use std::{mem, string}; extern crate kernel32;
use std::ptr::null; #[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) { unsafe fn flush_volume(name: &str) {
let name_buffer = name.to_c_str().as_ptr(); let name_wide = name.to_wide_null();
if 0x00000003 == GetDriveTypeA(name_buffer) { // DRIVE_FIXED if kernel32::GetDriveTypeW(name_wide.as_ptr()) == winapi::DRIVE_FIXED {
let sliced_name = &name[..name.len() - 1]; // eliminate trailing backslash let sliced_name = &name[..name.len() - 1]; // eliminate trailing backslash
let sliced_name_buffer = sliced_name.to_c_str().as_ptr(); match OpenOptions::new().write(true).open(sliced_name) {
match CreateFileA(sliced_name_buffer, Ok(file) => if kernel32::FlushFileBuffers(file.as_raw_handle()) == 0 {
0xC0000000, // GENERIC_WRITE crash!(kernel32::GetLastError() as i32, "failed to flush file buffer");
0x00000003, // FILE_SHARE_WRITE, },
null(), Err(e) => crash!(e.raw_os_error().unwrap_or(1), "failed to create volume handle")
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, winapi::HANDLE) {
unsafe fn find_first_volume() -> (String, *const libc::c_void) { let mut name: [winapi::WCHAR; winapi::MAX_PATH] = mem::uninitialized();
let mut name: [libc::c_char; 260] = mem::uninitialized(); // MAX_PATH let handle = kernel32::FindFirstVolumeW(name.as_mut_ptr(), name.len() as winapi::DWORD);
match FindFirstVolumeA(name.as_mut_ptr(), if handle == winapi::INVALID_HANDLE_VALUE {
name.len() as libc::uint32_t) { crash!(kernel32::GetLastError() as i32, "failed to find first volume");
-1 => { // INVALID_HANDLE_VALUE
crash!(GetLastError(), "failed to find first volume");
}
handle => {
(string::raw::from_buf(name.as_ptr() as *const u8), handle)
}
} }
(String::from_wide_null(&name), handle)
} }
#[allow(unused_unsafe)]
unsafe fn find_all_volumes() -> Vec<String> { unsafe fn find_all_volumes() -> Vec<String> {
match find_first_volume() { let (first_volume, next_volume_handle) = find_first_volume();
(first_volume, next_volume_handle) => { let mut volumes = vec![first_volume];
let mut volumes = vec![first_volume]; loop {
loop { let mut name: [winapi::WCHAR; winapi::MAX_PATH] = mem::uninitialized();
let mut name: [libc::c_char; 260] = mem::uninitialized(); // MAX_PATH if kernel32::FindNextVolumeW(
match FindNextVolumeA(next_volume_handle, next_volume_handle, name.as_mut_ptr(), name.len() as winapi::DWORD
name.as_mut_ptr(), ) == 0 {
name.len() as libc::uint32_t) { match kernel32::GetLastError() {
0 => { winapi::ERROR_NO_MORE_FILES => {
match GetLastError() { kernel32::FindVolumeClose(next_volume_handle);
0x12 => { // ERROR_NO_MORE_FILES return volumes
FindVolumeClose(next_volume_handle); // ignore FindVolumeClose() failures },
break; 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(); let volumes = find_all_volumes();
for vol in volumes.iter() { for vol in &volumes {
flush_volume(&vol); flush_volume(vol);
} }
0 0
} }

View file

@ -20,8 +20,10 @@ use std::str::{from_utf8};
static NAME: &'static str = "test"; static NAME: &'static str = "test";
// TODO: decide how to handle non-UTF8 input for all the utils // 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 { pub fn uumain(_: Vec<String>) -> i32 {
let args = args_os().collect::<Vec<OsString>>(); 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]>>(); let args = args.iter().map(|a| a.to_bytes().unwrap()).collect::<Vec<&[u8]>>();
if args.len() == 0 { if args.len() == 0 {
return 2; return 2;
@ -382,32 +384,26 @@ fn path(path: &[u8], cond: PathCondition) -> bool {
#[cfg(windows)] #[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool { fn path(path: &[u8], cond: PathCondition) -> bool {
use std::old_io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe}; use std::fs::metadata;
use std::old_io::fs::{stat}; let path = from_utf8(path).unwrap();
use std::old_path::{Path}; let stat = match metadata(path) {
let path = match Path::new_opt(path) {
Some(p) => p,
None => return false,
};
let stat = match stat(&path) {
Ok(s) => s, Ok(s) => s,
_ => return false, _ => return false,
}; };
match cond { match cond {
BlockSpecial => stat.kind == TypeBlockSpecial, PathCondition::BlockSpecial => false,
CharacterSpecial => false, PathCondition::CharacterSpecial => false,
Directory => stat.kind == TypeDirectory, PathCondition::Directory => stat.is_dir(),
Exists => true, PathCondition::Exists => true,
Regular => stat.kind == TypeFile, PathCondition::Regular => stat.is_file(),
GroupIDFlag => false, PathCondition::GroupIDFlag => false,
SymLink => false, PathCondition::SymLink => false,
FIFO => stat.kind == TypeNamedPipe, PathCondition::FIFO => false,
Readable => false, // TODO PathCondition::Readable => false, // TODO
Socket => false, // TODO? PathCondition::Socket => false,
NonEmpty => stat.size > 0, PathCondition::NonEmpty => stat.len() > 0,
UserIDFlag => false, PathCondition::UserIDFlag => false,
Writable => false, // TODO PathCondition::Writable => false, // TODO
Executable => 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. * 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::mem;
use std::io::Write; 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 { pub unsafe fn getusername() -> String {
// usernames can only be up to 104 characters in windows let mut buffer: [winapi::WCHAR; winapi::UNLEN as usize + 1] = mem::uninitialized();
let mut buffer: [libc::c_char; 105] = mem::uninitialized(); let mut len = buffer.len() as winapi::DWORD;
if advapi32::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
if !GetUserNameA(buffer.as_mut_ptr(), &mut (buffer.len() as libc::uint32_t)) == 0 { crash!(1, "failed to get username");
crash!(1, "username is too long");
} }
String::from_utf8_lossy(::std::ffi::CStr::from_ptr(buffer.as_ptr()).to_bytes()).to_string() String::from_wide(&buffer[..len as usize - 1])
} }