mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #1641 from sylvestre/install-owner-group
feature(install) - add group & owner support + a refactor
This commit is contained in:
commit
94571ecf8f
13 changed files with 428 additions and 215 deletions
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/chgrp.rs"
|
path = "src/chgrp.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs"] }
|
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs", "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" }
|
||||||
walkdir = "2.2"
|
walkdir = "2.2"
|
||||||
|
|
||||||
|
|
|
@ -11,23 +11,18 @@
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
pub use uucore::entries;
|
pub use uucore::entries;
|
||||||
use uucore::fs::resolve_relative_path;
|
use uucore::fs::resolve_relative_path;
|
||||||
use uucore::libc::{self, gid_t, lchown};
|
use uucore::libc::gid_t;
|
||||||
|
use uucore::perms::{wrap_chgrp, Verbosity};
|
||||||
|
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use std::io::Error as IOError;
|
|
||||||
use std::io::Result as IOResult;
|
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::Metadata;
|
use std::fs::Metadata;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
static SYNTAX: &str =
|
static SYNTAX: &str =
|
||||||
"chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE...";
|
"chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE...";
|
||||||
static SUMMARY: &str = "Change the group of each FILE to GROUP.";
|
static SUMMARY: &str = "Change the group of each FILE to GROUP.";
|
||||||
|
@ -165,14 +160,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
executor.exec()
|
executor.exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum Verbosity {
|
|
||||||
Silent,
|
|
||||||
Changes,
|
|
||||||
Verbose,
|
|
||||||
Normal,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Chgrper {
|
struct Chgrper {
|
||||||
dest_gid: gid_t,
|
dest_gid: gid_t,
|
||||||
bit_flag: u8,
|
bit_flag: u8,
|
||||||
|
@ -201,23 +188,6 @@ impl Chgrper {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chgrp<P: AsRef<Path>>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
if follow {
|
|
||||||
libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
|
||||||
} else {
|
|
||||||
lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if ret == 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(IOError::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
|
fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
|
||||||
// TODO: is there an equivalent on Windows?
|
// TODO: is there an equivalent on Windows?
|
||||||
|
@ -269,7 +239,24 @@ impl Chgrper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = self.wrap_chgrp(path, &meta, follow_arg);
|
let ret = match wrap_chgrp(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_gid,
|
||||||
|
follow_arg,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
show_info!("{}", n);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity != Verbosity::Silent {
|
||||||
|
show_info!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if !self.recursive {
|
if !self.recursive {
|
||||||
ret
|
ret
|
||||||
|
@ -297,8 +284,22 @@ impl Chgrper {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = self.wrap_chgrp(path, &meta, follow);
|
ret = match wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()) {
|
||||||
|
Ok(n) => {
|
||||||
|
if n != "" {
|
||||||
|
show_info!("{}", n);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity != Verbosity::Silent {
|
||||||
|
show_info!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,50 +325,4 @@ impl Chgrper {
|
||||||
};
|
};
|
||||||
Some(meta)
|
Some(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_chgrp<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
|
|
||||||
use self::Verbosity::*;
|
|
||||||
let mut ret = 0;
|
|
||||||
let dest_gid = self.dest_gid;
|
|
||||||
let path = path.as_ref();
|
|
||||||
if let Err(e) = self.chgrp(path, dest_gid, follow) {
|
|
||||||
match self.verbosity {
|
|
||||||
Silent => (),
|
|
||||||
_ => {
|
|
||||||
show_info!("changing group of '{}': {}", path.display(), e);
|
|
||||||
if self.verbosity == Verbose {
|
|
||||||
println!(
|
|
||||||
"failed to change group of {} from {} to {}",
|
|
||||||
path.display(),
|
|
||||||
entries::gid2grp(meta.gid()).unwrap(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = 1;
|
|
||||||
} else {
|
|
||||||
let changed = dest_gid != meta.gid();
|
|
||||||
if changed {
|
|
||||||
match self.verbosity {
|
|
||||||
Changes | Verbose => {
|
|
||||||
println!(
|
|
||||||
"changed group of {} from {} to {}",
|
|
||||||
path.display(),
|
|
||||||
entries::gid2grp(meta.gid()).unwrap(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
} else if self.verbosity == Verbose {
|
|
||||||
println!(
|
|
||||||
"group of {} retained as {}",
|
|
||||||
path.display(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/chown.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs"] }
|
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs", "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" }
|
||||||
walkdir = "2.2"
|
walkdir = "2.2"
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
pub use uucore::entries::{self, Group, Locate, Passwd};
|
pub use uucore::entries::{self, Group, Locate, Passwd};
|
||||||
use uucore::fs::resolve_relative_path;
|
use uucore::fs::resolve_relative_path;
|
||||||
use uucore::libc::{self, gid_t, lchown, uid_t};
|
use uucore::libc::{gid_t, uid_t};
|
||||||
|
use uucore::perms::{wrap_chown, Verbosity};
|
||||||
|
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
@ -22,15 +23,9 @@ use walkdir::WalkDir;
|
||||||
use std::fs::{self, Metadata};
|
use std::fs::{self, Metadata};
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::io::Result as IOResult;
|
|
||||||
|
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
static ABOUT: &str = "change file owner and group";
|
static ABOUT: &str = "change file owner and group";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
@ -304,14 +299,6 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum Verbosity {
|
|
||||||
Silent,
|
|
||||||
Changes,
|
|
||||||
Verbose,
|
|
||||||
Normal,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum IfFrom {
|
enum IfFrom {
|
||||||
All,
|
All,
|
||||||
User(u32),
|
User(u32),
|
||||||
|
@ -349,29 +336,6 @@ impl Chowner {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chown<P: AsRef<Path>>(
|
|
||||||
&self,
|
|
||||||
path: P,
|
|
||||||
duid: uid_t,
|
|
||||||
dgid: gid_t,
|
|
||||||
follow: bool,
|
|
||||||
) -> IOResult<()> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
if follow {
|
|
||||||
libc::chown(s.as_ptr(), duid, dgid)
|
|
||||||
} else {
|
|
||||||
lchown(s.as_ptr(), duid, dgid)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if ret == 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
||||||
let path = root.as_ref();
|
let path = root.as_ref();
|
||||||
|
@ -408,7 +372,27 @@ impl Chowner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = if self.matched(meta.uid(), meta.gid()) {
|
let ret = if self.matched(meta.uid(), meta.gid()) {
|
||||||
self.wrap_chown(path, &meta, follow_arg)
|
match wrap_chown(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_uid,
|
||||||
|
self.dest_gid,
|
||||||
|
follow_arg,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
if n != "" {
|
||||||
|
show_info!("{}", n);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity != Verbosity::Silent {
|
||||||
|
show_info!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -443,7 +427,27 @@ impl Chowner {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = self.wrap_chown(path, &meta, follow);
|
ret = match wrap_chown(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_uid,
|
||||||
|
self.dest_gid,
|
||||||
|
follow,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
if n != "" {
|
||||||
|
show_info!("{}", n);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity != Verbosity::Silent {
|
||||||
|
show_info!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -471,58 +475,6 @@ impl Chowner {
|
||||||
Some(meta)
|
Some(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_chown<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
|
|
||||||
use self::Verbosity::*;
|
|
||||||
let mut ret = 0;
|
|
||||||
let dest_uid = self.dest_uid.unwrap_or_else(|| meta.uid());
|
|
||||||
let dest_gid = self.dest_gid.unwrap_or_else(|| meta.gid());
|
|
||||||
let path = path.as_ref();
|
|
||||||
if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) {
|
|
||||||
match self.verbosity {
|
|
||||||
Silent => (),
|
|
||||||
_ => {
|
|
||||||
show_info!("changing ownership of '{}': {}", path.display(), e);
|
|
||||||
if self.verbosity == Verbose {
|
|
||||||
println!(
|
|
||||||
"failed to change ownership of {} from {}:{} to {}:{}",
|
|
||||||
path.display(),
|
|
||||||
entries::uid2usr(meta.uid()).unwrap(),
|
|
||||||
entries::gid2grp(meta.gid()).unwrap(),
|
|
||||||
entries::uid2usr(dest_uid).unwrap(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = 1;
|
|
||||||
} else {
|
|
||||||
let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
|
|
||||||
if changed {
|
|
||||||
match self.verbosity {
|
|
||||||
Changes | Verbose => {
|
|
||||||
println!(
|
|
||||||
"changed ownership of {} from {}:{} to {}:{}",
|
|
||||||
path.display(),
|
|
||||||
entries::uid2usr(meta.uid()).unwrap(),
|
|
||||||
entries::gid2grp(meta.gid()).unwrap(),
|
|
||||||
entries::uid2usr(dest_uid).unwrap(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
} else if self.verbosity == Verbose {
|
|
||||||
println!(
|
|
||||||
"ownership of {} retained as {}:{}",
|
|
||||||
path.display(),
|
|
||||||
entries::uid2usr(dest_uid).unwrap(),
|
|
||||||
entries::gid2grp(dest_gid).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
||||||
match self.filter {
|
match self.filter {
|
||||||
|
|
|
@ -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,7 +16,11 @@ mod mode;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use uucore::entries::{grp2gid, usr2uid};
|
||||||
|
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
|
@ -27,6 +31,8 @@ pub struct Behavior {
|
||||||
main_function: MainFunction,
|
main_function: MainFunction,
|
||||||
specified_mode: Option<u32>,
|
specified_mode: Option<u32>,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
|
owner: String,
|
||||||
|
group: String,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +132,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,14 +145,16 @@ 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
|
|
||||||
Arg::with_name(OPT_OWNER)
|
Arg::with_name(OPT_OWNER)
|
||||||
.short("o")
|
.short("o")
|
||||||
.long(OPT_OWNER)
|
.long(OPT_OWNER)
|
||||||
.help("(unimplemented) set ownership (super-user only)")
|
.help("set ownership (super-user only)")
|
||||||
.value_name("OWNER")
|
.value_name("OWNER")
|
||||||
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
// TODO implement flag
|
||||||
|
@ -176,6 +184,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 +224,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,10 +268,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) {
|
|
||||||
Err("--owner, -o")
|
|
||||||
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
||||||
Err("--preserve-timestamps, -p")
|
Err("--preserve-timestamps, -p")
|
||||||
} else if matches.is_present(OPT_STRIP) {
|
} else if matches.is_present(OPT_STRIP) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,6 +338,8 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
main_function,
|
main_function,
|
||||||
specified_mode,
|
specified_mode,
|
||||||
suffix: backup_suffix.to_string(),
|
suffix: backup_suffix.to_string(),
|
||||||
|
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
|
||||||
|
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
|
||||||
verbose: matches.is_present(OPT_VERBOSE),
|
verbose: matches.is_present(OPT_VERBOSE),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -400,22 +398,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 +487,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 +499,54 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.owner != "" {
|
||||||
|
let meta = match fs::metadata(to) {
|
||||||
|
Ok(meta) => meta,
|
||||||
|
Err(f) => crash!(1, "{}", f.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let owner_id = match usr2uid(&b.owner) {
|
||||||
|
Ok(g) => g,
|
||||||
|
_ => crash!(1, "no such user: {}", b.owner),
|
||||||
|
};
|
||||||
|
let gid = meta.gid();
|
||||||
|
match wrap_chown(
|
||||||
|
to.as_path(),
|
||||||
|
&meta,
|
||||||
|
Some(owner_id),
|
||||||
|
Some(gid),
|
||||||
|
false,
|
||||||
|
Verbosity::Normal,
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
if n != "" {
|
||||||
|
show_info!("{}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => show_info!("{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
match wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal) {
|
||||||
|
Ok(n) => {
|
||||||
|
if n != "" {
|
||||||
|
show_info!("{}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => show_info!("{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
show_info!("'{}' -> '{}'", from.display(), to.display());
|
show_info!("'{}' -> '{}'", from.display(), to.display());
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ entries = ["libc"]
|
||||||
fs = ["libc"]
|
fs = ["libc"]
|
||||||
mode = ["libc"]
|
mode = ["libc"]
|
||||||
parse_time = []
|
parse_time = []
|
||||||
|
perms = ["libc"]
|
||||||
process = ["libc"]
|
process = ["libc"]
|
||||||
signals = []
|
signals = []
|
||||||
utf8 = []
|
utf8 = []
|
||||||
|
|
|
@ -13,11 +13,15 @@ pub mod zero_copy;
|
||||||
// ** non-windows
|
// ** non-windows
|
||||||
#[cfg(all(not(windows), feature = "mode"))]
|
#[cfg(all(not(windows), feature = "mode"))]
|
||||||
pub mod mode;
|
pub mod mode;
|
||||||
|
|
||||||
// ** unix-only
|
// ** unix-only
|
||||||
#[cfg(all(unix, feature = "entries"))]
|
#[cfg(all(unix, feature = "entries"))]
|
||||||
pub mod entries;
|
pub mod entries;
|
||||||
|
#[cfg(all(unix, feature = "perms"))]
|
||||||
|
pub mod perms;
|
||||||
#[cfg(all(unix, feature = "process"))]
|
#[cfg(all(unix, feature = "process"))]
|
||||||
pub mod process;
|
pub mod process;
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
||||||
pub mod signals;
|
pub mod signals;
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
|
|
182
src/uucore/src/lib/features/perms.rs
Normal file
182
src/uucore/src/lib/features/perms.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
pub use crate::features::entries;
|
||||||
|
use libc::{self, gid_t, lchown, uid_t};
|
||||||
|
|
||||||
|
use std::io::Error as IOError;
|
||||||
|
use std::io::Result as IOResult;
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::fs::Metadata;
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// The various level of verbosity
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
pub enum Verbosity {
|
||||||
|
Silent,
|
||||||
|
Changes,
|
||||||
|
Verbose,
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actually perform the change of group on a path
|
||||||
|
fn chgrp<P: AsRef<Path>>(path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||||
|
let ret = unsafe {
|
||||||
|
if follow {
|
||||||
|
libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
||||||
|
} else {
|
||||||
|
lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ret == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IOError::last_os_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform the change of group on a path
|
||||||
|
/// with the various options
|
||||||
|
/// and error messages management
|
||||||
|
pub fn wrap_chgrp<P: AsRef<Path>>(
|
||||||
|
path: P,
|
||||||
|
meta: &Metadata,
|
||||||
|
dest_gid: gid_t,
|
||||||
|
follow: bool,
|
||||||
|
verbosity: Verbosity,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
use self::Verbosity::*;
|
||||||
|
let path = path.as_ref();
|
||||||
|
let mut out: String = String::new();
|
||||||
|
|
||||||
|
if let Err(e) = chgrp(path, dest_gid, follow) {
|
||||||
|
match verbosity {
|
||||||
|
Silent => (),
|
||||||
|
_ => {
|
||||||
|
out = format!("changing group of '{}': {}", path.display(), e);
|
||||||
|
if verbosity == Verbose {
|
||||||
|
out = format!(
|
||||||
|
"{}\nfailed to change group of '{}' from {} to {}",
|
||||||
|
out,
|
||||||
|
path.display(),
|
||||||
|
entries::gid2grp(meta.gid()).unwrap(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(out);
|
||||||
|
} else {
|
||||||
|
let changed = dest_gid != meta.gid();
|
||||||
|
if changed {
|
||||||
|
match verbosity {
|
||||||
|
Changes | Verbose => {
|
||||||
|
out = format!(
|
||||||
|
"changed group of '{}' from {} to {}",
|
||||||
|
path.display(),
|
||||||
|
entries::gid2grp(meta.gid()).unwrap(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
} else if verbosity == Verbose {
|
||||||
|
out = format!(
|
||||||
|
"group of '{}' retained as {}",
|
||||||
|
path.display(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actually perform the change of owner on a path
|
||||||
|
fn chown<P: AsRef<Path>>(path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||||
|
let ret = unsafe {
|
||||||
|
if follow {
|
||||||
|
libc::chown(s.as_ptr(), duid, dgid)
|
||||||
|
} else {
|
||||||
|
lchown(s.as_ptr(), duid, dgid)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ret == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IOError::last_os_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform the change of owner on a path
|
||||||
|
/// with the various options
|
||||||
|
/// and error messages management
|
||||||
|
pub fn wrap_chown<P: AsRef<Path>>(
|
||||||
|
path: P,
|
||||||
|
meta: &Metadata,
|
||||||
|
dest_uid: Option<u32>,
|
||||||
|
dest_gid: Option<u32>,
|
||||||
|
follow: bool,
|
||||||
|
verbosity: Verbosity,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
use self::Verbosity::*;
|
||||||
|
let dest_uid = dest_uid.unwrap_or_else(|| meta.uid());
|
||||||
|
let dest_gid = dest_gid.unwrap_or_else(|| meta.gid());
|
||||||
|
let path = path.as_ref();
|
||||||
|
let mut out: String = String::new();
|
||||||
|
|
||||||
|
if let Err(e) = chown(path, dest_uid, dest_gid, follow) {
|
||||||
|
match verbosity {
|
||||||
|
Silent => (),
|
||||||
|
_ => {
|
||||||
|
out = format!("changing ownership of '{}': {}", path.display(), e);
|
||||||
|
if verbosity == Verbose {
|
||||||
|
out = format!(
|
||||||
|
"{}\nfailed to change ownership of '{}' from {}:{} to {}:{}",
|
||||||
|
out,
|
||||||
|
path.display(),
|
||||||
|
entries::uid2usr(meta.uid()).unwrap(),
|
||||||
|
entries::gid2grp(meta.gid()).unwrap(),
|
||||||
|
entries::uid2usr(dest_uid).unwrap(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(out);
|
||||||
|
} else {
|
||||||
|
let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
|
||||||
|
if changed {
|
||||||
|
match verbosity {
|
||||||
|
Changes | Verbose => {
|
||||||
|
out = format!(
|
||||||
|
"changed ownership of '{}' from {}:{} to {}:{}",
|
||||||
|
path.display(),
|
||||||
|
entries::uid2usr(meta.uid()).unwrap(),
|
||||||
|
entries::gid2grp(meta.gid()).unwrap(),
|
||||||
|
entries::uid2usr(dest_uid).unwrap(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
} else if verbosity == Verbose {
|
||||||
|
out = format!(
|
||||||
|
"ownership of '{}' retained as {}:{}",
|
||||||
|
path.display(),
|
||||||
|
entries::uid2usr(dest_uid).unwrap(),
|
||||||
|
entries::gid2grp(dest_gid).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
|
@ -53,6 +53,8 @@ pub use crate::features::mode;
|
||||||
// ** unix-only
|
// ** unix-only
|
||||||
#[cfg(all(unix, feature = "entries"))]
|
#[cfg(all(unix, feature = "entries"))]
|
||||||
pub use crate::features::entries;
|
pub use crate::features::entries;
|
||||||
|
#[cfg(all(unix, feature = "perms"))]
|
||||||
|
pub use crate::features::perms;
|
||||||
#[cfg(all(unix, feature = "process"))]
|
#[cfg(all(unix, feature = "process"))]
|
||||||
pub use crate::features::process;
|
pub use crate::features::process;
|
||||||
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
||||||
|
|
|
@ -110,8 +110,7 @@ fn test_reference() {
|
||||||
.arg("--reference=/etc/passwd")
|
.arg("--reference=/etc/passwd")
|
||||||
.arg("/etc")
|
.arg("/etc")
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\n")
|
.stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\nfailed to change group of '/etc' from root to root");
|
||||||
.stdout_is("failed to change group of /etc from root to root\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use rust_users::get_effective_uid;
|
||||||
|
|
||||||
extern crate chown;
|
extern crate chown;
|
||||||
// pub use self::uu_chown::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_passgrp {
|
mod test_passgrp {
|
||||||
|
@ -345,7 +346,9 @@ fn test_chown_recursive() {
|
||||||
// As seems to be a configuration issue, ignoring it
|
// As seems to be a configuration issue, ignoring it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert!(result.stdout.contains("ownership of a/a retained as"));
|
|
||||||
|
assert!(result.stderr.contains("ownership of 'a/a' retained as"));
|
||||||
|
assert!(result.stderr.contains("ownership of 'z/y' retained as"));
|
||||||
assert!(result.success);
|
assert!(result.success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,3 +382,17 @@ fn test_root_preserve() {
|
||||||
.stderr
|
.stderr
|
||||||
.contains("chown: it is dangerous to operate recursively"));
|
.contains("chown: it is dangerous to operate recursively"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn test_big_p() {
|
||||||
|
if get_effective_uid() != 0 {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-RP")
|
||||||
|
.arg("bin")
|
||||||
|
.arg("/proc/self/cwd")
|
||||||
|
.fails()
|
||||||
|
.stderr_is(
|
||||||
|
"chown: changing ownership of '/proc/self/cwd': Operation not permitted (os error 1)\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
use rust_users::*;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -206,6 +207,66 @@ 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);
|
||||||
|
let result = ucmd
|
||||||
|
.arg(file)
|
||||||
|
.arg("--group")
|
||||||
|
.arg(gid.to_string())
|
||||||
|
.arg(format!("{}/{}", dir, file))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
println!("stderr = {:?}", result.stderr);
|
||||||
|
println!("stdout = {:?}", result.stdout);
|
||||||
|
|
||||||
|
if is_ci() && result.stderr.contains("error: no such group:") {
|
||||||
|
// In the CI, some server are failing to return the group.
|
||||||
|
// As seems to be a configuration issue, ignoring it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(result.success);
|
||||||
|
assert!(at.file_exists(file));
|
||||||
|
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_target_new_file_with_owner() {
|
||||||
|
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 uid = get_effective_uid();
|
||||||
|
|
||||||
|
at.touch(file);
|
||||||
|
at.mkdir(dir);
|
||||||
|
let result = ucmd
|
||||||
|
.arg(file)
|
||||||
|
.arg("--owner")
|
||||||
|
.arg(uid.to_string())
|
||||||
|
.arg(format!("{}/{}", dir, file))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
println!("stderr = {:?}", result.stderr);
|
||||||
|
println!("stdout = {:?}", result.stdout);
|
||||||
|
|
||||||
|
if is_ci() && result.stderr.contains("error: no such user:") {
|
||||||
|
// In the CI, some server are failing to return the user id.
|
||||||
|
// As seems to be a configuration issue, ignoring it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(result.success);
|
||||||
|
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