mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
feature(install): install group support
This commit is contained in:
parent
763de90fda
commit
015e18731f
3 changed files with 62 additions and 33 deletions
|
@ -20,7 +20,7 @@ path = "src/install.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
libc = ">= 0.2"
|
libc = ">= 0.2"
|
||||||
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["mode"] }
|
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["mode", "perms"] }
|
||||||
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -16,6 +16,9 @@ mod mode;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use uucore::perms::{wrap_chgrp, Verbosity};
|
||||||
|
use uucore::entries::grp2gid;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
@ -27,6 +30,7 @@ pub struct Behavior {
|
||||||
main_function: MainFunction,
|
main_function: MainFunction,
|
||||||
specified_mode: Option<u32>,
|
specified_mode: Option<u32>,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
|
group: String,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +130,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST")
|
.help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
|
||||||
Arg::with_name(OPT_GROUP)
|
Arg::with_name(OPT_GROUP)
|
||||||
.short("g")
|
.short("g")
|
||||||
.long(OPT_GROUP)
|
.long(OPT_GROUP)
|
||||||
.help("(unimplemented) set group ownership, instead of process's current group")
|
.help("set group ownership, instead of process's current group")
|
||||||
.value_name("GROUP")
|
.value_name("GROUP")
|
||||||
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_MODE)
|
Arg::with_name(OPT_MODE)
|
||||||
|
@ -139,6 +143,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long(OPT_MODE)
|
.long(OPT_MODE)
|
||||||
.help("set permission mode (as in chmod), instead of rwxr-xr-x")
|
.help("set permission mode (as in chmod), instead of rwxr-xr-x")
|
||||||
.value_name("MODE")
|
.value_name("MODE")
|
||||||
|
.takes_value(true)
|
||||||
|
.min_values(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
// TODO implement flag
|
||||||
|
@ -176,6 +182,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long(OPT_SUFFIX)
|
.long(OPT_SUFFIX)
|
||||||
.help("(unimplemented) override the usual backup suffix")
|
.help("(unimplemented) override the usual backup suffix")
|
||||||
.value_name("SUFFIX")
|
.value_name("SUFFIX")
|
||||||
|
.takes_value(true)
|
||||||
|
.min_values(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
// TODO implement flag
|
||||||
|
@ -214,7 +222,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.help("(unimplemented) set security context of files and directories")
|
.help("(unimplemented) set security context of files and directories")
|
||||||
.value_name("CONTEXT")
|
.value_name("CONTEXT")
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true))
|
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
|
||||||
.get_matches_from(args);
|
.get_matches_from(args);
|
||||||
|
|
||||||
let paths: Vec<String> = matches
|
let paths: Vec<String> = matches
|
||||||
|
@ -258,8 +266,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||||
Err("--compare, -C")
|
Err("--compare, -C")
|
||||||
} else if matches.is_present(OPT_CREATED) {
|
} else if matches.is_present(OPT_CREATED) {
|
||||||
Err("-D")
|
Err("-D")
|
||||||
} else if matches.is_present(OPT_GROUP) {
|
|
||||||
Err("--group, -g")
|
|
||||||
} else if matches.is_present(OPT_OWNER) {
|
} else if matches.is_present(OPT_OWNER) {
|
||||||
Err("--owner, -o")
|
Err("--owner, -o")
|
||||||
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
||||||
|
@ -292,7 +298,7 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||||
/// In event of failure, returns an integer intended as a program return code.
|
/// In event of failure, returns an integer intended as a program return code.
|
||||||
///
|
///
|
||||||
fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
let main_function = if matches.is_present("directory") {
|
let main_function = if matches.is_present(OPT_DIRECTORY) {
|
||||||
MainFunction::Directory
|
MainFunction::Directory
|
||||||
} else {
|
} else {
|
||||||
MainFunction::Standard
|
MainFunction::Standard
|
||||||
|
@ -310,11 +316,6 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
show_error!(
|
|
||||||
"option '--mode' requires an argument\n \
|
|
||||||
Try '{} --help' for more information.",
|
|
||||||
executable!()
|
|
||||||
);
|
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,11 +327,6 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
match matches.value_of(OPT_SUFFIX) {
|
match matches.value_of(OPT_SUFFIX) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => {
|
||||||
show_error!(
|
|
||||||
"option '--suffix' requires an argument\n\
|
|
||||||
Try '{} --help' for more information.",
|
|
||||||
executable!()
|
|
||||||
);
|
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,10 +334,13 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
"~"
|
"~"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let group = matches.value_of(OPT_GROUP).unwrap_or_else(|| "");
|
||||||
|
|
||||||
Ok(Behavior {
|
Ok(Behavior {
|
||||||
main_function,
|
main_function,
|
||||||
specified_mode,
|
specified_mode,
|
||||||
suffix: backup_suffix.to_string(),
|
suffix: backup_suffix.to_string(),
|
||||||
|
group: group.to_string(),
|
||||||
verbose: matches.is_present(OPT_VERBOSE),
|
verbose: matches.is_present(OPT_VERBOSE),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -400,22 +399,16 @@ fn is_new_file_path(path: &Path) -> bool {
|
||||||
/// Returns an integer intended as a program return code.
|
/// Returns an integer intended as a program return code.
|
||||||
///
|
///
|
||||||
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
||||||
if paths.len() < 2 {
|
let sources = &paths[0..paths.len() - 1]
|
||||||
println!("{} requires at least 2 arguments.", executable!());
|
.iter()
|
||||||
1
|
.map(PathBuf::from)
|
||||||
} else {
|
.collect::<Vec<_>>();
|
||||||
let sources = &paths[0..paths.len() - 1]
|
let target = Path::new(paths.last().unwrap());
|
||||||
.iter()
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let target = Path::new(paths.last().unwrap());
|
|
||||||
|
|
||||||
if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
|
if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
|
||||||
/* If the target already exist or directly creatable */
|
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
||||||
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
} else {
|
||||||
} else {
|
copy_files_into_dir(sources, &target.to_path_buf(), &b)
|
||||||
copy_files_into_dir(sources, &target.to_path_buf(), &b)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,7 +488,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||||
|
|
||||||
if let Err(err) = io_result {
|
if let Err(err) = io_result {
|
||||||
show_error!(
|
show_error!(
|
||||||
"install: cannot install ‘{}’ to ‘{}’: {}",
|
"cannot install ‘{}’ to ‘{}’: {}",
|
||||||
from.display(),
|
from.display(),
|
||||||
to.display(),
|
to.display(),
|
||||||
err
|
err
|
||||||
|
@ -507,6 +500,20 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.group != "" {
|
||||||
|
let meta = match fs::metadata(to) {
|
||||||
|
Ok(meta) => meta,
|
||||||
|
Err(f) => crash!(1, "{}", f.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let group_id = match grp2gid(&b.group) {
|
||||||
|
Ok(g) => g,
|
||||||
|
_ => crash!(1, "no such group: {}", b.group),
|
||||||
|
};
|
||||||
|
wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
show_info!("'{}' -> '{}'", from.display(), to.display());
|
show_info!("'{}' -> '{}'", from.display(), to.display());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use rust_users::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_install_help() {
|
fn test_install_help() {
|
||||||
|
@ -206,6 +207,27 @@ fn test_install_target_new_file() {
|
||||||
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_target_new_file_with_group() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file = "test_install_target_new_filer_file_j";
|
||||||
|
let dir = "test_install_target_new_file_dir_j";
|
||||||
|
let gid = get_effective_gid();
|
||||||
|
|
||||||
|
at.touch(file);
|
||||||
|
at.mkdir(dir);
|
||||||
|
ucmd.arg(file)
|
||||||
|
.arg("--group")
|
||||||
|
.arg(gid.to_string())
|
||||||
|
.arg(format!("{}/{}", dir, file))
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
|
assert!(at.file_exists(file));
|
||||||
|
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_install_target_new_file_failing_nonexistent_parent() {
|
fn test_install_target_new_file_failing_nonexistent_parent() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue