summaryrefslogtreecommitdiffstats
path: root/vendor/gix-worktree/src/checkout/entry.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-worktree/src/checkout/entry.rs')
-rw-r--r--vendor/gix-worktree/src/checkout/entry.rs166
1 files changed, 0 insertions, 166 deletions
diff --git a/vendor/gix-worktree/src/checkout/entry.rs b/vendor/gix-worktree/src/checkout/entry.rs
deleted file mode 100644
index 524cf90f2..000000000
--- a/vendor/gix-worktree/src/checkout/entry.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use std::{fs::OpenOptions, io::Write, path::Path};
-
-use bstr::BStr;
-use gix_hash::oid;
-use gix_index::{entry::Stat, Entry};
-use io_close::Close;
-
-use crate::Cache;
-
-pub struct Context<'a, Find> {
- pub find: &'a mut Find,
- pub path_cache: &'a mut Cache,
- pub buf: &'a mut Vec<u8>,
-}
-
-#[cfg_attr(not(unix), allow(unused_variables))]
-pub fn checkout<Find, E>(
- entry: &mut Entry,
- entry_path: &BStr,
- Context { find, path_cache, buf }: Context<'_, Find>,
- crate::checkout::Options {
- fs: gix_fs::Capabilities {
- symlink,
- executable_bit,
- ..
- },
- destination_is_initially_empty,
- overwrite_existing,
- ..
- }: crate::checkout::Options,
-) -> Result<usize, crate::checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E>,
- E: std::error::Error + Send + Sync + 'static,
-{
- let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| crate::checkout::Error::IllformedUtf8 {
- path: entry_path.to_owned(),
- })?;
- let is_dir = Some(entry.mode == gix_index::entry::Mode::COMMIT || entry.mode == gix_index::entry::Mode::DIR);
- let dest = path_cache.at_path(dest_relative, is_dir, &mut *find)?.path();
-
- let object_size = match entry.mode {
- gix_index::entry::Mode::FILE | gix_index::entry::Mode::FILE_EXECUTABLE => {
- let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
- err,
- oid: entry.id,
- path: dest.to_path_buf(),
- })?;
-
- #[cfg_attr(not(unix), allow(unused_mut))]
- let mut options = open_options(dest, destination_is_initially_empty, overwrite_existing);
- let needs_executable_bit = executable_bit && entry.mode == gix_index::entry::Mode::FILE_EXECUTABLE;
- #[cfg(unix)]
- if needs_executable_bit && destination_is_initially_empty {
- use std::os::unix::fs::OpenOptionsExt;
- // Note that these only work if the file was newly created, but won't if it's already
- // existing, possibly without the executable bit set. Thus we do this only if the file is new.
- options.mode(0o777);
- }
-
- let mut file = try_write_or_unlink(dest, overwrite_existing, |p| options.open(p))?;
- file.write_all(obj.data)?;
-
- // For possibly existing, overwritten files, we must change the file mode explicitly.
- #[cfg(unix)]
- if needs_executable_bit && !destination_is_initially_empty {
- use std::os::unix::fs::PermissionsExt;
- let mut perm = std::fs::symlink_metadata(dest)?.permissions();
- perm.set_mode(0o777);
- std::fs::set_permissions(dest, perm)?;
- }
- // NOTE: we don't call `file.sync_all()` here knowing that some filesystems don't handle this well.
- // revisit this once there is a bug to fix.
- entry.stat = Stat::from_fs(&file.metadata()?)?;
- file.close()?;
- obj.data.len()
- }
- gix_index::entry::Mode::SYMLINK => {
- let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
- err,
- oid: entry.id,
- path: dest.to_path_buf(),
- })?;
- let symlink_destination = gix_path::try_from_byte_slice(obj.data)
- .map_err(|_| crate::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;
-
- if symlink {
- try_write_or_unlink(dest, overwrite_existing, |p| {
- gix_fs::symlink::create(symlink_destination, p)
- })?;
- } else {
- let mut file = try_write_or_unlink(dest, overwrite_existing, |p| {
- open_options(p, destination_is_initially_empty, overwrite_existing).open(dest)
- })?;
- file.write_all(obj.data)?;
- file.close()?;
- }
-
- entry.stat = Stat::from_fs(&std::fs::symlink_metadata(dest)?)?;
- obj.data.len()
- }
- gix_index::entry::Mode::DIR => todo!(),
- gix_index::entry::Mode::COMMIT => todo!(),
- _ => unreachable!(),
- };
- Ok(object_size)
-}
-
-/// Note that this works only because we assume to not race ourselves when symlinks are involved, and we do this by
-/// delaying symlink creation to the end and will always do that sequentially.
-/// It's still possible to fall for a race if other actors create symlinks in our path, but that's nothing to defend against.
-fn try_write_or_unlink<T>(
- path: &Path,
- overwrite_existing: bool,
- op: impl Fn(&Path) -> std::io::Result<T>,
-) -> std::io::Result<T> {
- if overwrite_existing {
- match op(path) {
- Ok(res) => Ok(res),
- Err(err) if gix_fs::symlink::is_collision_error(&err) => {
- try_unlink_path_recursively(path, &std::fs::symlink_metadata(path)?)?;
- op(path)
- }
- Err(err) => Err(err),
- }
- } else {
- op(path)
- }
-}
-
-fn try_unlink_path_recursively(path: &Path, path_meta: &std::fs::Metadata) -> std::io::Result<()> {
- if path_meta.is_dir() {
- std::fs::remove_dir_all(path)
- } else if path_meta.file_type().is_symlink() {
- gix_fs::symlink::remove(path)
- } else {
- std::fs::remove_file(path)
- }
-}
-
-#[cfg(not(debug_assertions))]
-fn debug_assert_dest_is_no_symlink(_path: &Path) {}
-
-/// This is a debug assertion as we expect the machinery calling this to prevent this possibility in the first place
-#[cfg(debug_assertions)]
-fn debug_assert_dest_is_no_symlink(path: &Path) {
- if let Ok(meta) = path.metadata() {
- debug_assert!(
- !meta.file_type().is_symlink(),
- "BUG: should not ever allow to overwrite/write-into the target of a symbolic link: {}",
- path.display()
- );
- }
-}
-
-fn open_options(path: &Path, destination_is_initially_empty: bool, overwrite_existing: bool) -> OpenOptions {
- if overwrite_existing || !destination_is_initially_empty {
- debug_assert_dest_is_no_symlink(path);
- }
- let mut options = gix_features::fs::open_options_no_follow();
- options
- .create_new(destination_is_initially_empty && !overwrite_existing)
- .create(!destination_is_initially_empty || overwrite_existing)
- .write(true);
- options
-}