Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/uu/chgrp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ path = "src/chgrp.rs"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2"

[[bin]]
name = "chgrp"
Expand Down
231 changes: 26 additions & 205 deletions src/uu/chgrp/src/chgrp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,21 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) COMFOLLOW Chgrper RFILE RFILE's derefer dgid nonblank nonprint nonprinting
// spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting

#[macro_use]
extern crate uucore;
pub use uucore::entries;
use uucore::fs::resolve_relative_path;
use uucore::libc::gid_t;
use uucore::perms::{wrap_chgrp, Verbosity};
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::perms::{
ChownExecutor, IfFrom, Verbosity, VerbosityLevel, FTS_COMFOLLOW, FTS_LOGICAL, FTS_PHYSICAL,
};

use clap::{App, Arg};

extern crate walkdir;
use walkdir::WalkDir;

use std::fs;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::path::Path;
use uucore::InvalidEncodingHandling;

static ABOUT: &str = "Change the group of each FILE to GROUP.";
Expand Down Expand Up @@ -55,23 +51,20 @@ pub mod options {
pub static ARG_FILES: &str = "FILE";
}

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

fn usage() -> String {
fn get_usage() -> String {
format!(
"{0} [OPTION]... GROUP FILE...\n {0} [OPTION]... --reference=RFILE FILE...",
uucore::execution_phrase()
)
}

pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();

let usage = usage();
let usage = get_usage();

let mut app = uu_app().usage(&usage[..]);

Expand Down Expand Up @@ -140,58 +133,55 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if recursive {
if bit_flag == FTS_PHYSICAL {
if derefer == 1 {
show_error!("-R --dereference requires -H or -L");
return 1;
return Err(USimpleError::new(1, "-R --dereference requires -H or -L"));
}
derefer = 0;
}
} else {
bit_flag = FTS_PHYSICAL;
}

let verbosity = if matches.is_present(options::verbosity::CHANGES) {
Verbosity::Changes
let verbosity_level = if matches.is_present(options::verbosity::CHANGES) {
VerbosityLevel::Changes
} else if matches.is_present(options::verbosity::SILENT)
|| matches.is_present(options::verbosity::QUIET)
{
Verbosity::Silent
VerbosityLevel::Silent
} else if matches.is_present(options::verbosity::VERBOSE) {
Verbosity::Verbose
VerbosityLevel::Verbose
} else {
Verbosity::Normal
VerbosityLevel::Normal
};

let dest_gid = if let Some(file) = matches.value_of(options::REFERENCE) {
match fs::metadata(&file) {
Ok(meta) => Some(meta.gid()),
Err(e) => {
show_error!("failed to get attributes of '{}': {}", file, e);
return 1;
}
}
fs::metadata(&file)
.map(|meta| Some(meta.gid()))
.map_err_context(|| format!("failed to get attributes of '{}'", file))?
} else {
let group = matches.value_of(options::ARG_GROUP).unwrap_or_default();
if group.is_empty() {
None
} else {
match entries::grp2gid(group) {
Ok(g) => Some(g),
_ => {
show_error!("invalid group: {}", group);
return 1;
}
_ => return Err(USimpleError::new(1, format!("invalid group: '{}'", group))),
}
}
};

let executor = Chgrper {
let executor = ChownExecutor {
bit_flag,
dest_gid,
verbosity,
verbosity: Verbosity {
groups_only: true,
level: verbosity_level,
},
recursive,
dereference: derefer != 0,
preserve_root,
files,
filter: IfFrom::All,
dest_uid: None,
};
executor.exec()
}
Expand Down Expand Up @@ -275,172 +265,3 @@ pub fn uu_app() -> App<'static, 'static> {
.help("traverse every symbolic link to a directory encountered"),
)
}

struct Chgrper {
dest_gid: Option<gid_t>,
bit_flag: u8,
verbosity: Verbosity,
files: Vec<String>,
recursive: bool,
preserve_root: bool,
dereference: bool,
}

macro_rules! unwrap {
($m:expr, $e:ident, $err:block) => {
match $m {
Ok(meta) => meta,
Err($e) => $err,
}
};
}

impl Chgrper {
fn exec(&self) -> i32 {
let mut ret = 0;
for f in &self.files {
ret |= self.traverse(f);
}
ret
}

#[cfg(windows)]
fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
// TODO: is there an equivalent on Windows?
false
}

#[cfg(unix)]
fn is_bind_root<P: AsRef<Path>>(&self, path: P) -> bool {
if let (Ok(given), Ok(root)) = (fs::metadata(path), fs::metadata("/")) {
given.dev() == root.dev() && given.ino() == root.ino()
} else {
// FIXME: not totally sure if it's okay to just ignore an error here
false
}
}

fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
let path = root.as_ref();
let meta = match self.obtain_meta(path, follow_arg) {
Some(m) => m,
_ => return 1,
};

// Prohibit only if:
// (--preserve-root and -R present) &&
// (
// (argument is not symlink && resolved to be '/') ||
// (argument is symlink && should follow argument && resolved to be '/')
// )
if self.recursive && self.preserve_root {
let may_exist = if follow_arg {
path.canonicalize().ok()
} else {
let real = resolve_relative_path(path);
if real.is_dir() {
Some(real.canonicalize().expect("failed to get real path"))
} else {
Some(real.into_owned())
}
};

if let Some(p) = may_exist {
if p.parent().is_none() || self.is_bind_root(p) {
show_error!("it is dangerous to operate recursively on '/'");
show_error!("use --no-preserve-root to override this failsafe");
return 1;
}
}
}

let ret = match wrap_chgrp(
path,
&meta,
self.dest_gid,
follow_arg,
self.verbosity.clone(),
) {
Ok(n) => {
if !n.is_empty() {
show_error!("{}", n);
}
0
}
Err(e) => {
if self.verbosity != Verbosity::Silent {
show_error!("{}", e);
}
1
}
};

if !self.recursive {
ret
} else {
ret | self.dive_into(&root)
}
}

fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
let mut ret = 0;
let root = root.as_ref();
let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
let entry = unwrap!(entry, e, {
ret = 1;
show_error!("{}", e);
continue;
});
let path = entry.path();
let meta = match self.obtain_meta(path, follow) {
Some(m) => m,
_ => {
ret = 1;
continue;
}
};

ret = match wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()) {
Ok(n) => {
if !n.is_empty() {
show_error!("{}", n);
}
0
}
Err(e) => {
if self.verbosity != Verbosity::Silent {
show_error!("{}", e);
}
1
}
}
}

ret
}

fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
use self::Verbosity::*;
let path = path.as_ref();
let meta = if follow {
unwrap!(path.metadata(), e, {
match self.verbosity {
Silent => (),
_ => show_error!("cannot access '{}': {}", path.display(), e),
}
return None;
})
} else {
unwrap!(path.symlink_metadata(), e, {
match self.verbosity {
Silent => (),
_ => show_error!("cannot dereference '{}': {}", path.display(), e),
}
return None;
})
};
Some(meta)
}
}
2 changes: 0 additions & 2 deletions src/uu/chown/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ path = "src/chown.rs"

[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
glob = "0.3.0"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2"

[[bin]]
name = "chown"
Expand Down
Loading