Skip to content

Commit a6c2194

Browse files
committed
feat(import): Add support for setting permissions
1 parent 4ce4df5 commit a6c2194

5 files changed

Lines changed: 98 additions & 1 deletion

File tree

src/cli/import.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ pub async fn run(config: &Config, cache: Option<&Cache>, args: Args) -> crate::R
232232
if let Err(err) = track_collection.write_tags() {
233233
log::error!("Failed to write tags: {err}");
234234
}
235+
236+
#[cfg(unix)]
237+
if let Err(err) = track_collection.set_permissions(&cloned_config) {
238+
log::error!("Failed to set permissions: {err}");
239+
}
235240
}
236241
});
237242

src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,20 @@ pub struct PathTemplateConfig {
379379
pub compilation_format: String,
380380
}
381381

382+
/// The import configuration struct.
383+
#[cfg(unix)]
384+
#[expect(missing_copy_implementations)]
385+
#[expect(clippy::struct_field_names)]
386+
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
387+
pub struct ImportConfig {
388+
/// User ID to set.
389+
pub set_uid: Option<u32>,
390+
/// Group ID to set.
391+
pub set_gid: Option<u32>,
392+
/// File mode to set.
393+
pub set_mode: Option<u32>,
394+
}
395+
382396
/// The main configuration struct.
383397
#[derive(Debug, Clone, Deserialize, Serialize)]
384398
pub struct Config {
@@ -392,6 +406,10 @@ pub struct Config {
392406
pub weights: DistanceWeights,
393407
/// UI configuration.
394408
pub user_interface: UiConfig,
409+
/// Import configuration.
410+
#[cfg(unix)]
411+
#[serde(default)]
412+
pub import: ImportConfig,
395413
}
396414

397415
impl Default for Config {

src/taggedfilecollection.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,29 @@ impl TaggedFileCollection {
294294

295295
Ok(())
296296
}
297+
298+
/// Set permission for all tracks in this collection.
299+
///
300+
/// # Errors
301+
///
302+
/// Returns an error if any of the underlying file system operations fail.
303+
#[cfg(unix)]
304+
pub fn set_permissions(&mut self, config: &Config) -> crate::Result<()> {
305+
for track in &mut self
306+
.media
307+
.iter_mut()
308+
.flat_map(|media| media.tracks.iter_mut())
309+
{
310+
util::set_file_permissions(
311+
&track.path,
312+
config.import.set_uid,
313+
config.import.set_gid,
314+
config.import.set_mode,
315+
)?;
316+
}
317+
318+
Ok(())
319+
}
297320
}
298321

299322
impl IntoIterator for TaggedFileCollection {

src/util/fs.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ use std::collections::BinaryHeap;
1212
use std::ffi::OsStr;
1313
use std::fs;
1414
use std::io;
15+
use std::os::unix::{self, fs::PermissionsExt};
1516
use std::path::{Path, PathBuf};
1617

1718
/// An iterator that recursively walks through a directory structure and yields a tuple `(path,
1819
/// dirs, files)` for each directory it visits.
19-
///
2020
/// This struct is created by [`walk_dir`]. See its documentation for more.
2121
pub struct DirWalk {
2222
/// Queued paths that will be visited next.
@@ -117,3 +117,52 @@ pub fn move_file<S: AsRef<Path>, D: AsRef<Path>>(source: S, destination: D) -> c
117117

118118
Ok(())
119119
}
120+
121+
/// Set file owner.
122+
#[cfg(unix)]
123+
pub fn set_file_permissions<S: AsRef<Path>>(
124+
source: S,
125+
uid: Option<u32>,
126+
gid: Option<u32>,
127+
mode: Option<u32>,
128+
) -> crate::Result<()> {
129+
unix::fs::chown(&source, uid, gid)?;
130+
match (uid, gid) {
131+
(Some(owner), Some(group)) => {
132+
log::info!(
133+
"Changed owner/group for {} to {owner}:{group}.",
134+
source.as_ref().display()
135+
);
136+
}
137+
(Some(owner), None) => {
138+
log::info!(
139+
"Changed owner for {} to {owner}.",
140+
source.as_ref().display()
141+
);
142+
}
143+
(None, Some(group)) => {
144+
log::info!(
145+
"Changed group for {} to {group}.",
146+
source.as_ref().display()
147+
);
148+
}
149+
_ => (),
150+
}
151+
152+
if let Some(new_mode) = mode {
153+
let file = fs::File::open(&source)?;
154+
let permissions = file.metadata()?.permissions();
155+
let old_mode = permissions.mode();
156+
157+
if permissions.mode() != new_mode {
158+
let permissions = fs::Permissions::from_mode(new_mode);
159+
file.set_permissions(permissions)?;
160+
log::info!(
161+
"Permission for {} changed from {old_mode:o} to {new_mode:o}.",
162+
source.as_ref().display()
163+
);
164+
}
165+
}
166+
167+
Ok(())
168+
}

src/util/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ mod keyed_binheap;
1414
mod testing;
1515
mod time;
1616

17+
#[cfg(unix)]
18+
pub use fs::set_file_permissions;
1719
pub use fs::{move_file, walk_dir};
1820
pub use keyed_binheap::KeyedBinaryHeap;
1921
#[cfg(any(test, feature = "dev"))]

0 commit comments

Comments
 (0)