git_policy_cli/
shadow_create.rs1use super::shadow_git::{REPOSITORY_REDIRECT_VARIABLES, shadow_repository_path};
23use super::capture_invocation::InvocationCapture;
25use super::shadow_config::{format_shadow_config, read_repository_format, repository_format_from_snapshot};
27use super::shadow_links::link_shadow_entries;
29use super::conclusion_copy::copy_conclusion_state;
31use super::shadow_refs::write_shadow_refs;
32use super::private_storage::create_private_directory;
34use super::transaction_git::{GitContext, GitFailure, GitRequest, run_git_checked};
36use std::path::{Path, PathBuf};
38use std::ffi::OsString;
40
41fn overwrite_private(path: &Path, bytes: &[u8]) -> std::io::Result<()> {
52 std::fs::write(path, bytes)?;
53 return set_private_file(path);
54}
55
56#[cfg(unix)]
66fn set_private_file(path: &Path) -> std::io::Result<()> {
67 use std::os::unix::fs::PermissionsExt;
68 return std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600));
69}
70
71#[cfg(windows)]
74fn set_private_file(_path: &Path) -> std::io::Result<()> {
75 return Ok(());
76}
77
78fn create_parent(shadow: &Path) -> std::io::Result<()> {
88 let parent: &Path = shadow.parent().unwrap_or(shadow);
89 if let Some(grandparent) = parent.parent() {
90 ensure_private_directory(grandparent)?;
91 }
92 return ensure_private_directory(parent);
93}
94
95pub fn ensure_private_directory(directory: &Path) -> std::io::Result<()> {
106 match create_private_directory(directory) {
107 Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists && directory.is_dir() => return Ok(()),
108 other => return other,
109 }
110}
111
112pub fn create_shadow_repository(
123 context: &GitContext,
124 cwd: &Path,
125 capture: &InvocationCapture,
126 transaction_id: &str,
127 transaction_directory: &Path,
128) -> Result<PathBuf, GitFailure> {
129 let layout = &capture.layout;
130 let shadow: PathBuf = shadow_repository_path(layout.common_dir.as_path(), transaction_id);
131 create_parent(shadow.as_path())
132 .map_err(|error| { return GitFailure(format!("creating {} failed: {error}", shadow.display())); })?;
133 let format_argument: String = format!("--object-format={}", layout.object_format);
134 let mut arguments: Vec<String> = vec![
135 String::from("init"),
136 String::from("--bare"),
137 String::from("--quiet"),
138 String::from("--template="),
139 format_argument,
140 ];
141 if layout.ref_format == super::transaction_journal::RefFormat::Reftable {
142 arguments.push(String::from("--ref-format=reftable"));
143 }
144 arguments.push(shadow.display().to_string());
145 let mut init: GitRequest = GitRequest::new(layout.common_dir.as_path(), &arguments);
146 init.without_prefix = true;
147 init.unset = REPOSITORY_REDIRECT_VARIABLES.iter().map(|name| { return OsString::from(*name); }).collect();
148 run_git_checked(context, &init)?;
149 set_private_directory_mode(shadow.as_path())
150 .map_err(|error| { return GitFailure(format!("protecting {} failed: {error}", shadow.display())); })?;
151 let format = match layout.config_snapshot.as_ref().and_then(repository_format_from_snapshot) {
152 Some(from_snapshot) => from_snapshot,
153 None => read_repository_format(context, layout.common_dir.as_path())?,
154 };
155 let config_text: String =
156 format_shadow_config(&format, layout.common_dir.as_path(), layout.repository_root.as_path());
157 overwrite_private(shadow.join("config").as_path(), config_text.as_bytes())
158 .map_err(|error| { return GitFailure(format!("writing the shadow config failed: {error}")); })?;
159 overwrite_private(
160 shadow.join("objects").join("info").join("alternates").as_path(),
161 format!("{}\n", layout.object_directory.display()).as_bytes(),
162 )
163 .map_err(|error| { return GitFailure(format!("writing the shadow alternates failed: {error}")); })?;
164 link_shadow_entries(shadow.as_path(), layout.common_dir.as_path(), layout.git_dir.as_path())
165 .map_err(|error| { return GitFailure(format!("linking the shadow entries failed: {error}")); })?;
166 copy_conclusion_state(
167 context,
168 cwd,
169 layout.git_dir.as_path(),
170 shadow.as_path(),
171 transaction_directory,
172 layout.ref_format,
173 )?;
174 write_shadow_refs(
175 context,
176 cwd,
177 shadow.as_path(),
178 layout.ref_format,
179 layout.target_ref.as_str(),
180 &layout.symbolic_head,
181 &capture.base,
182 )?;
183 return Ok(shadow);
184}
185
186#[cfg(unix)]
196fn set_private_directory_mode(path: &Path) -> std::io::Result<()> {
197 use std::os::unix::fs::PermissionsExt;
198 return std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o700));
199}
200
201#[cfg(windows)]
204fn set_private_directory_mode(_path: &Path) -> std::io::Result<()> {
205 return Ok(());
206}
207
208#[cfg(test)]
210#[path = "shadow_create_tests.rs"]
211mod tests;