aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/error.rs3
-rw-r--r--src/lib.rs2
-rw-r--r--src/main.rs24
-rw-r--r--src/minemod.rs21
-rw-r--r--src/util.rs40
5 files changed, 87 insertions, 3 deletions
diff --git a/src/error.rs b/src/error.rs
index 08539c0..86d758f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -32,6 +32,9 @@ pub enum Error {
UrlError(#[from] url::ParseError),
#[error("the mod ID '{0}' does not point to a single mod")]
AmbiguousModId(String),
+ #[cfg(unix)]
+ #[error("underlying Unix error")]
+ NixError(#[from] nix::errno::Errno),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
diff --git a/src/lib.rs b/src/lib.rs
index 9a3598f..bf9e3c9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,7 +16,7 @@ pub mod error;
pub mod game;
pub mod kvstore;
pub mod minemod;
-mod util;
+pub mod util;
pub mod world;
pub use baas::{Baas, Snapshot};
diff --git a/src/main.rs b/src/main.rs
index da04f9c..f42647a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@ use log::debug;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use modderbaas::{
- minemod::ModContainer, Baas, ContentDb, Downloader, MineMod, Snapshot, Source, World,
+ minemod::ModContainer, util, Baas, ContentDb, Downloader, MineMod, Snapshot, Source, World,
};
fn main() -> Result<()> {
@@ -52,6 +52,12 @@ fn main() -> Result<()> {
.short("n")
.long("dry-run")
.required(false),
+ )
+ .arg(
+ Arg::with_name("fix-permissions")
+ .short("p")
+ .long("fix-permissions")
+ .required(false),
),
)
.get_matches();
@@ -192,8 +198,9 @@ fn install_mods(
mods: &[&str],
matches: &ArgMatches,
) -> Result<()> {
- let target_dir = &Path::new(matches.value_of("target").unwrap());
+ let target_dir = Path::new(matches.value_of("target").unwrap());
let dry_run = matches.is_present("dry-run");
+ let fix_permissions = matches.is_present("fix-permissions");
let content_db = ContentDb::new();
let downloader = Downloader::new()?;
@@ -301,6 +308,19 @@ fn install_mods(
.install_to(target_dir)
.context(format!("Error installing '{}'", mod_id))?;
to_enable.extend(installed.mods()?);
+
+ #[cfg(unix)]
+ {
+ use nix::{
+ sys::stat,
+ unistd::{Gid, Uid},
+ };
+ if fix_permissions {
+ let perms = stat::stat(target_dir)?;
+ let (uid, gid) = (Uid::from_raw(perms.st_uid), Gid::from_raw(perms.st_gid));
+ util::chown_recursive(installed.path(), Some(uid), Some(gid))?;
+ }
+ }
}
for m in to_enable {
diff --git a/src/minemod.rs b/src/minemod.rs
index 9162907..f45b897 100644
--- a/src/minemod.rs
+++ b/src/minemod.rs
@@ -33,6 +33,11 @@ impl MineMod {
Ok(MineMod { path: path.into() })
}
+ /// Returns the path of this mod.
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
fn read_conf(&self) -> Result<HashMap<String, String>> {
let conf = self.path.join("mod.conf");
kvstore::read(&conf)
@@ -101,6 +106,11 @@ impl Modpack {
Ok(Modpack { path: path.into() })
}
+ /// Returns the path of this modpack.
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
fn conf(&self) -> Result<HashMap<String, String>> {
let conf = self.path.join("modpack.conf");
kvstore::read(&conf)
@@ -148,6 +158,9 @@ pub trait ModContainer: Any + fmt::Display {
/// Returns the name of the mod container.
fn name(&self) -> Result<String>;
+ /// Returns the on-disk path of this mod container.
+ fn path(&self) -> &Path;
+
/// Return all contained mods.
fn mods(&self) -> Result<Vec<MineMod>>;
@@ -160,6 +173,10 @@ impl ModContainer for MineMod {
self.mod_id()
}
+ fn path(&self) -> &Path {
+ self.path()
+ }
+
fn mods(&self) -> Result<Vec<MineMod>> {
Ok(vec![self.clone()])
}
@@ -175,6 +192,10 @@ impl ModContainer for Modpack {
self.name()
}
+ fn path(&self) -> &Path {
+ self.path()
+ }
+
fn mods(&self) -> Result<Vec<MineMod>> {
self.mods()
}
diff --git a/src/util.rs b/src/util.rs
index 24b8701..d5f9ec2 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,5 +1,19 @@
use std::{fs, io, path::Path};
+#[cfg(unix)]
+use nix::unistd::{self, Gid, Uid};
+
+use super::error::Result;
+
+#[cfg(not(unix))]
+pub mod nix {
+ //! Stub mod on non-unix systems.
+ pub mod unistd {
+ pub enum Uid {}
+ pub enum Gid {}
+ }
+}
+
/// Recursively copy the *contents* of the given directory to the given path.
pub fn copy_recursive<S: AsRef<Path>, D: AsRef<Path>>(source: S, destination: D) -> io::Result<()> {
copy_inner(source.as_ref(), destination.as_ref())
@@ -19,3 +33,29 @@ fn copy_inner(source: &Path, destination: &Path) -> io::Result<()> {
}
Ok(())
}
+
+/// Recursively change the owner of the given path to the given ones.
+///
+/// Note that this function only works on Unix systems. **It will panic on other systems!**
+pub fn chown_recursive<P: AsRef<Path>>(path: P, uid: Option<Uid>, gid: Option<Gid>) -> Result<()> {
+ chown_inner(path.as_ref(), uid, gid)
+}
+
+#[cfg(unix)]
+fn chown_inner(path: &Path, uid: Option<Uid>, gid: Option<Gid>) -> Result<()> {
+ unistd::chown(path, uid, gid)?;
+
+ let metadata = fs::metadata(path)?;
+ if metadata.is_dir() {
+ for item in fs::read_dir(path)? {
+ let item = item?;
+ chown_inner(&item.path(), uid, gid)?;
+ }
+ }
+ Ok(())
+}
+
+#[cfg(not(unix))]
+fn chown_inner(_: &Path, _: Option<Uid>, _: Option<Gid>) -> Result<()> {
+ panic!("chown() is not available on non-Unix systems!");
+}