diff options
Diffstat (limited to 'crates/shirabe-external-packages')
| -rw-r--r-- | crates/shirabe-external-packages/src/symfony/filesystem/filesystem.rs | 106 |
1 files changed, 71 insertions, 35 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/filesystem/filesystem.rs b/crates/shirabe-external-packages/src/symfony/filesystem/filesystem.rs index 295a7b86..450c7191 100644 --- a/crates/shirabe-external-packages/src/symfony/filesystem/filesystem.rs +++ b/crates/shirabe-external-packages/src/symfony/filesystem/filesystem.rs @@ -1,9 +1,9 @@ //! ref: composer/vendor/symfony/filesystem/Filesystem.php -// TODO(phase-c): PHP's box()/self::$lastError mechanism (captures the underlying OS error message -// from a failed native call via set_error_handler) is not modeled anywhere in this file. Every -// IOException message constructed below therefore omits the trailing low-level error string that -// PHP would append (e.g. "Failed to touch \"%s\": ".self::$lastError). +// TODO(phase-c): PHP's box()/self::$lastError mechanism captures the warning text emitted by the +// failed native call. The low-level error appended to the IOException messages below is instead the +// Rust io::Error text, which words the same errno differently (e.g. "Permission denied (os error +// 13)"). use crate::symfony::filesystem::exception::io_exception::IOException; use shirabe_php_shim::PhpMixed; @@ -146,9 +146,11 @@ impl Filesystem { continue; } - if !shirabe_php_shim::mkdir(&dir, mode, true) && !shirabe_php_shim::is_dir(&dir) { + if let Err(last_error) = shirabe_php_shim::mkdir_result(&dir, mode, true) + && !shirabe_php_shim::is_dir(&dir) + { return Err(IOException::new( - format!("Failed to create \"{}\": ", dir), + format!("Failed to create \"{}\": {}", dir, last_error), 0, None, Some(dir), @@ -200,13 +202,25 @@ impl Filesystem { for file in files { if shirabe_php_shim::is_link(&file) { // See https://bugs.php.net/52176 - if !(shirabe_php_shim::unlink(&file) - || !cfg!(windows) - || shirabe_php_shim::rmdir(&file)) - && shirabe_php_shim::file_exists(&file) - { + let unlinked = shirabe_php_shim::unlink_result(&file); + let mut last_error = unlinked.as_ref().err().map(ToString::to_string); + let mut removed = unlinked.is_ok() || !cfg!(windows); + if !removed { + match shirabe_php_shim::rmdir_result(&file) { + Ok(()) => { + last_error = None; + removed = true; + } + Err(e) => last_error = Some(e.to_string()), + } + } + if !removed && shirabe_php_shim::file_exists(&file) { return Err(IOException::new( - format!("Failed to remove symlink \"{}\": ", file), + format!( + "Failed to remove symlink \"{}\": {}", + file, + last_error.unwrap_or_default() + ), 0, None, None, @@ -237,27 +251,29 @@ impl Filesystem { (&entries).into_iter().map(|e| e.get_pathname()).collect(); Self::do_remove(child_paths, true)?; - if !shirabe_php_shim::rmdir(&file) && shirabe_php_shim::file_exists(&file) { + if let Err(last_error) = shirabe_php_shim::rmdir_result(&file) + && shirabe_php_shim::file_exists(&file) + { return Err(IOException::new( - format!("Failed to remove directory \"{}\": ", file), + format!("Failed to remove directory \"{}\": {}", file, last_error), + 0, + None, + None, + ) + .into()); + } + } else if let Err(last_error) = shirabe_php_shim::unlink_result(&file) { + let last_error = last_error.to_string(); + if last_error.contains("Permission denied") || shirabe_php_shim::file_exists(&file) + { + return Err(IOException::new( + format!("Failed to remove file \"{}\": {}", file, last_error), 0, None, None, ) .into()); } - } else if !shirabe_php_shim::unlink(&file) && shirabe_php_shim::file_exists(&file) { - // TODO(phase-c): PHP also throws when self::$lastError contains "Permission - // denied", even if file_exists() is now false (e.g. the file vanished between the - // failed unlink and this check). That OR-branch is dropped along with the general - // $lastError omission noted at the top of this file. - return Err(IOException::new( - format!("Failed to remove file \"{}\": ", file), - 0, - None, - None, - ) - .into()); } } Ok(()) @@ -293,21 +309,41 @@ impl Filesystem { self.remove(PhpMixed::String(target_dir.clone()))?; } - if !shirabe_php_shim::symlink(&origin_dir, &target_dir) { - return Self::link_exception(&origin_dir, &target_dir, "symbolic"); + if let Err(last_error) = shirabe_php_shim::symlink_result(&origin_dir, &target_dir) { + return Self::link_exception( + &origin_dir, + &target_dir, + "symbolic", + &last_error.to_string(), + ); } Ok(()) } - // TODO(phase-c): PHP special-cases a Windows error containing "error code(1314)" with a - // distinct "Do you have the required Administrator-rights?" message; that check (and the - // self::$lastError inspection it depends on) is not ported, so this always throws the generic - // message below. - fn link_exception(origin: &str, target: &str, link_type: &str) -> anyhow::Result<()> { + fn link_exception( + origin: &str, + target: &str, + link_type: &str, + last_error: &str, + ) -> anyhow::Result<()> { + // TODO(phase-c): the Windows io::Error text renders the error number as "(os error 1314)", + // so this substring never matches and the generic message below is thrown instead. + if !last_error.is_empty() && cfg!(windows) && last_error.contains("error code(1314)") { + return Err(IOException::new( + format!( + "Unable to create \"{}\" link due to error code 1314: 'A required privilege is not held by the client'. Do you have the required Administrator-rights?", + link_type + ), + 0, + None, + Some(target.to_string()), + ) + .into()); + } Err(IOException::new( format!( - "Failed to create \"{}\" link from \"{}\" to \"{}\": ", - link_type, origin, target + "Failed to create \"{}\" link from \"{}\" to \"{}\": {}", + link_type, origin, target, last_error ), 0, None, |
