aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock23
-rw-r--r--Cargo.toml3
-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
7 files changed, 113 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fe6497f..f100a34 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -450,6 +450,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -468,6 +477,7 @@ dependencies = [
"dirs",
"itertools",
"log",
+ "nix",
"once_cell",
"regex",
"scraper",
@@ -490,6 +500,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
+name = "nix"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 02650e4..158273a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,3 +25,6 @@ ureq = { version = "2.3.0", features = ["json"] }
url = { version = "2.2.2", features = ["serde"] }
uuid = { version = "0.8.2", features = ["v4"] }
zip = "0.5.13"
+
+[target.'cfg(unix)'.dependencies]
+nix = "0.23.0"
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!");
+}