diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/error.rs | 3 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 24 | ||||
| -rw-r--r-- | src/minemod.rs | 21 | ||||
| -rw-r--r-- | src/util.rs | 40 | 
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>; @@ -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!"); +} | 
