Skip to content

Commit 0f4671e

Browse files
committed
MGM: Add "recycle ls --project <path>" listing for recycle projects
Allow non-privileged users to list the contents of a recycle project bin by supplying an explicit path via "recycle ls --project <path>". The server reads the sys.forced.recycleid xattr on the given path and, if present, lists the corresponding rid:<val> recycle area. If the path does not belong to a recycle project, an error is returned to the client instead of silently falling back to the per-uid listing. Fixes EOS-6603
1 parent 0a0d366 commit 0f4671e

7 files changed

Lines changed: 158 additions & 38 deletions

File tree

common/grpc-proto

Submodule grpc-proto updated from 7e03aa6 to b37eec6

console/commands/helpers/RecycleHelper.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ RecycleHelper::ParseCommand(const char* arg)
100100
soption = option;
101101
ls->set_recycleid(soption);
102102
}
103+
} else if (soption == "--project") {
104+
if (!(option = tokenizer.GetToken())) {
105+
std::cerr << "error: --project requires a path argument" << std::endl;
106+
return false;
107+
}
108+
109+
soption = option;
110+
111+
if (soption.empty() || (soption[0] != '/')) {
112+
std::cerr << "error: --project path must be absolute" << std::endl;
113+
return false;
114+
}
115+
116+
ls->set_path(soption);
103117
} else if (soption == "-m") {
104118
ls->set_monitorfmt(true);
105119
} else if (soption == "-n") {

console/commands/native/recycle-proto-native.cc

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,100 @@
1212
namespace {
1313
std::string MakeRecycleHelp()
1414
{
15-
return "Usage: recycle [ls|purge|restore|config|project] [OPTIONS]\n\n"
16-
" [-m] print status of recycle bin\n"
17-
" ls [<date> [<limit>]] [-m] [-n] [--all] [--uid] [--rid <val>]\n"
18-
" list files in the recycle bin\n"
19-
" purge [--all] [--uid] [--rid <val>] <date> | -k <key>\n"
20-
" purge files by date or by key\n"
21-
" restore [-p] [-f|--force-original-name] [-r|--restore-versions] <key>\n"
22-
" undo deletion identified by recycle key\n"
23-
" project --path <path> [--acl <val>]\n"
24-
" setup recycle id for given top level directory\n"
25-
" config <key> <value>\n"
26-
" configure recycle policy (--dump, --add-bin, --remove-bin, "
27-
"--enable, etc.)\n";
15+
std::ostringstream oss;
16+
oss << "Usage: recycle [ls|purge|restore|config ...]\n"
17+
<< " provides recycle bin functionality\n"
18+
<< " recycle [-m]\n"
19+
<< " print status of recycle bin and config status if executed by root\n"
20+
<< " -m : display info in monitoring format\n"
21+
<< "\n"
22+
<< " recycle ls [<date> [<limit>]] [-m] [-n] [--all] [--uid] [--rid <val>] "
23+
"[--project <path>]\n"
24+
<< " list files in the recycle bin\n"
25+
<< " <date> : can be <year>, <year>/<month> or <year>/<month>/<day> "
26+
"or\n"
27+
<< " <year>/<month>/<day>/<index>\n"
28+
<< " <limit> : maximum number of entries to return when listing\n"
29+
<< " e.g.: recycle ls 2018/08/12 1000\n"
30+
<< " -m : display info in monitoring format\n"
31+
<< " -n : display numeric uid/gid(s) instead of names\n"
32+
<< " --all : display entries of all users - only if root or admin\n"
33+
<< " --uid : display entries for the current user id [default]\n"
34+
<< " --rid <val> : display entries corresponding to the given recycle id\n"
35+
<< " which represents the container id of the top directory\n"
36+
<< " e.g. recycle ls --rid 1001\n"
37+
<< " --project <path> : list the recycle project that the given path belongs\n"
38+
<< " to. The path must carry the sys.forced.recycleid\n"
39+
<< " extended attribute, otherwise an error is returned.\n"
40+
<< "\n"
41+
<< " recycle purge [--all] [--uid] [--rid <val>] <date> | -k <key>\n"
42+
<< " purge files in the recycle bin either by date or by key\n"
43+
<< " --all : purge entries of all users - only if root or admin\n"
44+
<< " --uid : purge entries for the current user [default]\n"
45+
<< " --rid <val> : purge entries corresponding to the given recycle id\n"
46+
<< " <date> : can be <year>, <year>/<month> or <year>/<month>/<day>\n"
47+
<< " and can't be used together with a recycle key\n"
48+
<< " -k <key> : purge only the given key\n"
49+
<< "\n"
50+
<< " recycle restore [-p] [-f|--force-original-name] [-r|--restore-versions] "
51+
"<key>\n"
52+
<< " undo the deletion identified by the recycle <key>\n"
53+
<< " -p : create all missing parent directories\n"
54+
<< " -f : move deleted files/dirs back to their original location\n"
55+
<< " (otherwise the key entry will have a <.inode> suffix)\n"
56+
<< " -r : restore all previous versions of a file\n"
57+
<< "\n"
58+
<< " recycle project --path <path> [--acl <val>]\n"
59+
<< " setup a recycle id that will group all the recycled paths from\n"
60+
<< " the given top level directory <path>. Optionally, specify a list\n"
61+
<< " of ACLs that are appended to the recycle location and control the\n"
62+
<< " access to the recycled entries. The recycle id is represented by the\n"
63+
<< " container id of <path> and is used to construct the recycle path:\n"
64+
<< " /eos/<instance>/proc/recycle/rid:<cid_value>/2025...\n"
65+
<< " ACL val is the usual string representation of ACLs e.g u:1234=rx\n"
66+
<< "\n"
67+
<< " recycle config <key> <value>\n"
68+
<< " where <key> and <value> need to be one of the following:\n"
69+
<< " --dump\n"
70+
<< " dump the current recycle policy configuration\n"
71+
<< " [--add-bin|--remove-bin] <sub-tree>\n"
72+
<< " --add-bin : enable recycle bin for deletion in <sub-tree>\n"
73+
<< " --remove-bin : disable recycle bin for <sub-tree>\n"
74+
<< " --enable <on/off>\n"
75+
<< " enable or disable the recycle bin functionality\n"
76+
<< " Default value: on\n"
77+
<< " --enforce <on/off>\n"
78+
<< " enforce default recycle bin location globally on the instance\n"
79+
<< " Default value: off\n"
80+
<< " --lifetime <seconds>\n"
81+
<< " configure FIFO lifetime for the recycle bin\n"
82+
<< " --ratio <0..1.0>\n"
83+
<< " configure the volume/inode keep ratio. E.g.: 0.8 means files\n"
84+
<< " will only be recycled if more than 80% of the volume/inodes\n"
85+
<< " quota is used. The low-watermark is by default 10% below the\n"
86+
<< " the given ratio.\n"
87+
<< " --size <value>[K|M|G]\n"
88+
<< " configure the quota for the maximum size of the recycle bin\n"
89+
<< " If no unit is set explicitly then bytes is assumed.\n"
90+
<< " --inodes <value>[K|M|G]\n"
91+
<< " configure the quota for the maximum number of inodes in the\n"
92+
<< " recycle bin.\n"
93+
<< " --dry-run <yes/no>\n"
94+
<< " when dry-run mode is enabled, no removal of entries is performed\n"
95+
<< " --collect-interval <seconds>\n"
96+
<< " how ofen the recycler collects new entries to be removed from\n"
97+
<< " the recycle bin. Default once per day i.e 86400 seconds.\n"
98+
<< " Change only for testing!\n"
99+
<< " --remove-interval <seconds>\n"
100+
<< " how often the recycler removes collected entries. The collected\n"
101+
<< " container ids to be removed are sharded and the removal is spread\n"
102+
<< " evenly across collect-interval/remove-interval slots. Default once\n"
103+
<< " every hour i.e. 3600. Change only for testing!\n"
104+
<< " Note: The last two parameters should be changed only for testing\n"
105+
<< " and while maintaining the following order:\n"
106+
<< " remove-interval << collection-interval\n"
107+
<< "\n";
108+
return oss.str();
28109
}
29110

30111
void ConfigureRecycleApp(CLI::App& app)

mgm/proc/user/RecycleCmd.cc

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,9 @@ RecycleCmd::ProcessRequest() noexcept
5353
rtype = "rid";
5454
}
5555

56-
rc = Recycle::Print(std_out, std_err, mVid, ls.monitorfmt(),
57-
!ls.numericids(), ls.fulldetails(), rtype,
58-
ls.recycleid(), ls.date(), nullptr, true,
59-
ls.maxentries());
56+
rc = Recycle::Print(std_out, std_err, mVid, ls.monitorfmt(), !ls.numericids(),
57+
ls.fulldetails(), rtype, ls.recycleid(), ls.date(), nullptr, true,
58+
ls.maxentries(), ls.path());
6059

6160
if (std_out.length()) {
6261
reply.set_std_out(std_out.c_str());
@@ -214,10 +213,9 @@ RecycleCmd::ProcessRequest(std::vector<std::map<std::string, std::string>>*
214213
rtype = "rid";
215214
}
216215

217-
rc = Recycle::Print(std_out, std_err, mVid, ls.monitorfmt(),
218-
!ls.numericids(), ls.fulldetails(), rtype,
219-
ls.recycleid(), ls.date(), rvec, true,
220-
ls.maxentries());
216+
rc = Recycle::Print(std_out, std_err, mVid, ls.monitorfmt(), !ls.numericids(),
217+
ls.fulldetails(), rtype, ls.recycleid(), ls.date(), rvec, true,
218+
ls.maxentries(), ls.path());
221219

222220
if (std_out.length()) {
223221
reply.set_std_out(std_out.c_str());

mgm/recycle/Recycle.cc

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -418,12 +418,10 @@ Recycle::Recycler(ThreadAssistant& assistant) noexcept
418418
//------------------------------------------------------------------------------
419419
int
420420
Recycle::Print(std::string& std_out, std::string& std_err,
421-
eos::common::VirtualIdentity& vid, bool monitoring,
422-
bool translateids, bool details,
423-
std::string_view display_type,
424-
std::string_view display_val,
425-
std::string_view date, Recycle::RecycleListing* rvec,
426-
bool whodeleted, int32_t maxentries)
421+
eos::common::VirtualIdentity& vid, bool monitoring, bool translateids,
422+
bool details, std::string_view display_type, std::string_view display_val,
423+
std::string_view date, Recycle::RecycleListing* rvec, bool whodeleted,
424+
int32_t maxentries, std::string_view project_path)
427425
{
428426
using namespace eos::common;
429427
XrdOucString uids;
@@ -441,10 +439,29 @@ Recycle::Print(std::string& std_out, std::string& std_err,
441439
}
442440
}
443441

444-
if ((display_type == "all") &&
445-
((!vid.uid) ||
446-
(vid.hasUid(eos::common::ADM_UID)) ||
447-
(vid.hasGid(eos::common::ADM_GID)))) {
442+
if (!project_path.empty()) {
443+
// An explicit --project path takes precedence over the listing type. The
444+
// path must belong to a recycle project (i.e. carry the
445+
// sys.forced.recycleid xattr) for both regular and admin users; otherwise
446+
// an error is returned to the client.
447+
std::string rid_val;
448+
XrdOucErrInfo lerror;
449+
std::string lpath(project_path);
450+
451+
if (gOFS->_attr_get(lpath.c_str(), lerror, mRootVid, "",
452+
Recycle::gRecycleIdXattrKey.c_str(), rid_val) ||
453+
rid_val.empty()) {
454+
std_err = SSTR("error: path \"" << lpath
455+
<< "\" is not part of a "
456+
"recycle project (missing "
457+
<< Recycle::gRecycleIdXattrKey << " xattr)");
458+
return EINVAL;
459+
}
460+
461+
printmap[SSTR("rid:" << rid_val)] = true;
462+
} else if ((display_type == "all") &&
463+
((!vid.uid) || (vid.hasUid(eos::common::ADM_UID)) ||
464+
(vid.hasGid(eos::common::ADM_GID)))) {
448465
// Add everything found in the recycle directory structure to the printmap
449466
std::string subdirs;
450467
XrdMgmOfsDirectory dirl;

mgm/recycle/Recycle.hh

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,17 @@ public:
117117
//! @param rvec a vector of maps with all recycle informations requested
118118
//! @param whodeleted - show who exectued a deletion
119119
//! @param maxentries - maximum number of entries to report
120+
//! @param project_path path used to detect a recycle project for
121+
//! non-privileged users (via the sys.forced.recycleid xattr)
120122
//!
121123
//! @return 0 if success, E2BIG if return list is limited
122124
//----------------------------------------------------------------------------
123125
static int Print(std::string& std_out, std::string& std_err,
124-
eos::common::VirtualIdentity& vid, bool monitoring,
125-
bool transalteids, bool details,
126-
std::string_view display_type,
127-
std::string_view display_val,
128-
std::string_view date = "", RecycleListing* rvec = 0,
129-
bool whodeleted = true, int32_t maxentries = 0);
126+
eos::common::VirtualIdentity& vid, bool monitoring, bool transalteids,
127+
bool details, std::string_view display_type,
128+
std::string_view display_val, std::string_view date = "",
129+
RecycleListing* rvec = 0, bool whodeleted = true,
130+
int32_t maxentries = 0, std::string_view project_path = "");
130131

131132
//----------------------------------------------------------------------------
132133
//! Restore an entry from the recycle bin to the original location

test/eos-instance-test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,15 @@ runtest "### Restore " unix SUCCESS "EOSROLE='-r 3 4'" eos recycle restore -f
11691169
runtest "### Rem " unix SUCCESS "" eos rm "/eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile"
11701170
runtest "### Purge " unix SUCCESS "EOSROLE='-r 3 4'" eos recycle purge --rid $rid -k `command eos -b root://$EOS_TEST_REDIRECTOR recycle ls --all | grep /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile | awk '{print $10}' | tail -1`
11711171
runtest "### NoFile " unix ERROR "" eos recycle ls --all | grep /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile
1172+
# Test "recycle ls --project <path>" listing for non-privileged users. A path
1173+
# that carries the sys.forced.recycleid xattr must list the project recycle
1174+
# bin, while a path without the xattr must return an error.
1175+
runtest "### Upload " unix SUCCESS "" upload "/etc/passwd" "/eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile"
1176+
runtest "### Rem " unix SUCCESS "" eos rm "/eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile"
1177+
runtest "### ProjLs " unix SUCCESS "EOSROLE='-r 3 4'" eos recycle ls --project /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle | grep /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile
1178+
runtest "### ProjLsSub " unix SUCCESS "EOSROLE='-r 3 4'" eos recycle ls --project /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/ | grep /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile
1179+
runtest "### ProjLsBad " unix ERROR "EOSROLE='-r 3 4'" eos recycle ls --project /eos/$EOS_TEST_INSTANCE/test/instancetest
1180+
runtest "### Purge " unix SUCCESS "EOSROLE='-r 3 4'" eos recycle purge --rid $rid -k `command eos -b root://$EOS_TEST_REDIRECTOR recycle ls --all | grep /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle/pfile | awk '{print $10}' | tail -1`
11721181
runtest "### Recycle " unix SUCCESS "" eos recycle config --remove-bin /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle
11731182
runtest "### Rem " unix SUCCESS "" eos rm -r /eos/$EOS_TEST_INSTANCE/test/instancetest/precycle
11741183

0 commit comments

Comments
 (0)