-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchown.cpp
More file actions
135 lines (120 loc) · 4.21 KB
/
chown.cpp
File metadata and controls
135 lines (120 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <grp.h>
#include <pwd.h>
#include <string>
#include <string_view>
#include <sys/stat.h>
#include <cfbox/args.hpp>
#include <cfbox/fs_util.hpp>
#include <cfbox/help.hpp>
#include <cfbox/error.hpp>
namespace {
constexpr cfbox::help::HelpEntry HELP = {
.name = "chown",
.version = CFBOX_VERSION_STRING,
.one_line = "change file owner and group",
.usage = "chown [-R] [-v] [OWNER][:GROUP] FILE...",
.options = " -R operate on files and directories recursively\n"
" -v output a diagnostic for every file processed\n"
" --reference=RFILE use RFILE's owner and group",
.extra = "OWNER and GROUP can be names or numeric IDs.",
};
struct OwnerSpec {
uid_t uid = static_cast<uid_t>(-1);
gid_t gid = static_cast<gid_t>(-1);
bool set_uid = false;
bool set_gid = false;
};
auto parse_owner_spec(std::string_view spec) -> OwnerSpec {
OwnerSpec result;
auto colon = spec.find(':');
if (colon == std::string_view::npos) {
result.set_uid = true;
std::string s(spec);
auto* pw = getpwnam(s.c_str());
if (pw) result.uid = pw->pw_uid;
else result.uid = static_cast<uid_t>(std::strtoul(s.c_str(), nullptr, 10));
} else {
auto owner_part = spec.substr(0, colon);
auto group_part = spec.substr(colon + 1);
if (!owner_part.empty()) {
result.set_uid = true;
std::string os(owner_part);
auto* pw = getpwnam(os.c_str());
if (pw) result.uid = pw->pw_uid;
else result.uid = static_cast<uid_t>(std::strtoul(os.c_str(), nullptr, 10));
}
if (!group_part.empty()) {
result.set_gid = true;
std::string gs(group_part);
auto* gr = getgrnam(gs.c_str());
if (gr) result.gid = gr->gr_gid;
else result.gid = static_cast<gid_t>(std::strtoul(gs.c_str(), nullptr, 10));
}
}
return result;
}
auto chown_one(const std::string& path, uid_t uid, gid_t gid, bool verbose) -> int {
auto result = cfbox::fs::chown(path, uid, gid);
if (!result) {
CFBOX_ERR("chown", "%s: %s", path.c_str(), result.error().msg.c_str());
return 1;
}
if (verbose) std::printf("ownership of '%s' changed\n", path.c_str());
return 0;
}
} // namespace
auto chown_main(int argc, char* argv[]) -> int {
auto parsed = cfbox::args::parse(argc, argv, {
cfbox::args::OptSpec{'R', false, "recursive"},
cfbox::args::OptSpec{'v', false, "verbose"},
cfbox::args::OptSpec{'\0', true, "reference"},
});
if (parsed.has_long("help")) { cfbox::help::print_help(HELP); return 0; }
if (parsed.has_long("version")) { cfbox::help::print_version(HELP); return 0; }
bool recursive = parsed.has('R');
bool verbose = parsed.has('v');
const auto& pos = parsed.positional();
OwnerSpec owner;
int files_start = 0;
if (parsed.has_long("reference")) {
auto rfile = parsed.get_long("reference");
if (!rfile) {
CFBOX_ERR("chown", "--reference requires an argument");
return 2;
}
struct stat st;
std::string rfile_str(*rfile);
if (stat(rfile_str.c_str(), &st) != 0) {
CFBOX_ERR("chown", "%s: %s", rfile_str.c_str(), std::strerror(errno));
return 1;
}
owner.uid = st.st_uid;
owner.gid = st.st_gid;
owner.set_uid = true;
owner.set_gid = true;
files_start = 0;
if (pos.empty()) {
CFBOX_ERR("chown", "missing operand");
return 2;
}
} else {
if (pos.size() < 2) {
CFBOX_ERR("chown", "missing operand");
return 2;
}
owner = parse_owner_spec(pos[0]);
files_start = 1;
}
int rc = 0;
for (size_t i = files_start; i < pos.size(); i++) {
uid_t uid = owner.set_uid ? owner.uid : static_cast<uid_t>(-1);
gid_t gid = owner.set_gid ? owner.gid : static_cast<gid_t>(-1);
cfbox::fs::for_each_entry(pos[i], recursive, [&](const std::string& p) {
if (chown_one(p, uid, gid, verbose) != 0) rc = 1;
});
}
return rc;
}