Skip to content

Commit 07e11cf

Browse files
committed
fix: set permissions of newly written loose objects to be similar to git.
Note that the current implementation lacks all of the sophistication that git applies, and doing this properly definitely takes more work as we would need to support `core.sharedRepository`. Further, our tempfile implementation doesn't allow the setup of file modes right when it matters, so that could mean quite some work to either workaround or contribute.
1 parent afe7faa commit 07e11cf

File tree

4 files changed

+68
-0
lines changed

4 files changed

+68
-0
lines changed

Diff for: gix-odb/src/store_impls/loose/write.rs

+28
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ impl crate::traits::Write for Store {
9898

9999
type CompressedTempfile = deflate::Write<NamedTempFile>;
100100

101+
/// Access
102+
impl Store {
103+
/// Return the path to the object with `id`.
104+
///
105+
/// Note that is may not exist yet.
106+
pub fn object_path(&self, id: &gix_hash::oid) -> PathBuf {
107+
loose::hash_path(id, self.path.clone())
108+
}
109+
}
110+
101111
impl Store {
102112
fn dest(&self) -> Result<hash::Write<CompressedTempfile>, Error> {
103113
Ok(hash::Write::new(
@@ -137,6 +147,24 @@ impl Store {
137147
return Ok(id);
138148
}
139149
}
150+
#[cfg(unix)]
151+
if let Ok(mut perm) = object_path.metadata().map(|m| m.permissions()) {
152+
use std::os::unix::fs::PermissionsExt;
153+
/// For now we assume the default with standard umask. This can be more sophisticated,
154+
/// but we have the bare minimum.
155+
fn comp_mode(_mode: u32) -> u32 {
156+
0o444
157+
}
158+
let new_mode = comp_mode(perm.mode());
159+
if (perm.mode() ^ new_mode) & !0o170000 != 0 {
160+
perm.set_mode(new_mode);
161+
std::fs::set_permissions(&object_path, perm).map_err(|err| Error::Io {
162+
source: err,
163+
message: "Failed to set permission bits",
164+
path: object_path.clone(),
165+
})?;
166+
}
167+
}
140168
res.map_err(|err| Error::Persist {
141169
source: err,
142170
target: object_path,

Diff for: gix-odb/tests/fixtures/generated-archives/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
repo_with_loose_objects.tar.xz

Diff for: gix-odb/tests/fixtures/repo_with_loose_objects.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
set -eu -o pipefail
3+
4+
git init -q
5+
6+
git checkout -b main
7+
touch this
8+
git add this
9+
git commit -q -m c1
10+
echo hello >> this
11+
git commit -q -am c2
12+

Diff for: gix-odb/tests/odb/store/loose.rs

+27
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,33 @@ mod write {
7171
Ok(())
7272
}
7373

74+
#[test]
75+
#[cfg(unix)]
76+
fn it_writes_objects_with_similar_permissions() -> crate::Result {
77+
let hk = gix_hash::Kind::Sha1;
78+
let git_store = loose::Store::at(
79+
gix_testtools::scripted_fixture_read_only("repo_with_loose_objects.sh")?.join(".git/objects"),
80+
hk,
81+
);
82+
let expected_perm = git_store
83+
.object_path(&gix_hash::ObjectId::empty_blob(hk))
84+
.metadata()?
85+
.permissions();
86+
87+
let tmp = tempfile::TempDir::new()?;
88+
let store = loose::Store::at(tmp.path(), hk);
89+
store.write_buf(gix_object::Kind::Blob, &[])?;
90+
let actual_perm = store
91+
.object_path(&gix_hash::ObjectId::empty_blob(hk))
92+
.metadata()?
93+
.permissions();
94+
assert_eq!(
95+
actual_perm, expected_perm,
96+
"we explicitly equalize permissions to be similar to what `git` would do"
97+
);
98+
Ok(())
99+
}
100+
74101
#[test]
75102
fn collisions_do_not_cause_failure() -> crate::Result {
76103
let dir = tempfile::tempdir()?;

0 commit comments

Comments
 (0)