Implement the quota feature for the POSIX driver.
Signed-off-by: Nithya Balachandran <nithya.balachandran@ibm.com>
return dbops.RemoveUser;
if (!Op.compare("GetUser"))
return dbops.GetUser;
+ if (!Op.compare("ListUsers"))
+ return dbops.ListUsers;
if (!Op.compare("InsertBucket"))
return dbops.InsertBucket;
if (!Op.compare("UpdateBucket"))
return ret;
}
+int DB::list_users(const DoutPrefixProvider *dpp,
+ const std::string& marker,
+ uint64_t max,
+ std::list<std::string>& keys,
+ bool *is_truncated)
+{
+ int ret = 0;
+ DBOpParams params = {};
+ InitializeParams(dpp, ¶ms);
+
+ params.op.user.uinfo.user_id = marker;
+ params.op.list_max_count = max;
+
+ ret = ProcessOp(dpp, "ListUsers", ¶ms);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) << "list_users failed with err:(" << ret <<") " << dendl;
+ goto out;
+ }
+ for (auto& entry : params.op.user.list_entries) {
+ keys.push_back(entry.user_id.to_str());
+ }
+
+out:
+ return ret;
+}
+
int DB::get_account(const DoutPrefixProvider *dpp,
const std::string& query_str, const std::string& query_str_val,
RGWAccountInfo& ainfo, map<string, bufferlist> *pattrs,
RGWUserInfo uinfo = {};
obj_version user_version;
rgw::sal::Attrs user_attrs;
+ std::list<RGWUserInfo> list_entries;
};
struct DBOpBucketInfo {
std::shared_ptr<class InsertUserOp> InsertUser;
std::shared_ptr<class RemoveUserOp> RemoveUser;
std::shared_ptr<class GetUserOp> GetUser;
+ std::shared_ptr<class ListUsersOp> ListUsers;
std::shared_ptr<class InsertBucketOp> InsertBucket;
std::shared_ptr<class UpdateBucketOp> UpdateBucket;
std::shared_ptr<class RemoveBucketOp> RemoveBucket;
}
};
+
+class ListUsersOp: virtual public DBOp {
+ static constexpr std::string_view Query = "SELECT \
+ UserID, Tenant, NS, DisplayName, UserEmail, \
+ AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+ SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+ System, PlacementName, PlacementStorageClass, PlacementTags, \
+ BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+ UserAttrs, UserVersion, UserVersionTag from '{}' where \
+ UserID >= {} ORDER BY UserID ASC LIMIT {} ";
+
+ public:
+ virtual ~ListUsersOp() {}
+
+ static std::string Schema(DBOpPrepareParams ¶ms) {
+ return fmt::format(Query,
+ params.user_table,
+ params.op.user.user_id,
+ params.op.list_max_count);
+ }
+};
+
class InsertBucketOp: virtual public DBOp {
private:
static constexpr std::string_view Query =
RGWObjVersionTracker *pobjv_tracker, RGWUserInfo* pold_info);
int remove_user(const DoutPrefixProvider *dpp,
RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv_tracker);
+ int list_users(const DoutPrefixProvider *dpp,
+ const std::string& marker,
+ uint64_t max,
+ std::list<std::string>& keys,
+ bool *is_truncated);
int get_account(const DoutPrefixProvider *dpp,
const std::string& query_str, const std::string& query_str_val,
RGWAccountInfo& ainfo, std::map<std::string, bufferlist> *pattrs,
op.user.user_version.ver = sqlite3_column_int(stmt, UserVersion);
op.user.user_version.tag = (const char*)sqlite3_column_text(stmt, UserVersionTag);
+ op.user.list_entries.push_back(op.user.uinfo);
return 0;
}
dbops.InsertUser = make_shared<SQLInsertUser>(&this->db, this->getDBname(), cct);
dbops.RemoveUser = make_shared<SQLRemoveUser>(&this->db, this->getDBname(), cct);
dbops.GetUser = make_shared<SQLGetUser>(&this->db, this->getDBname(), cct);
+ dbops.ListUsers = make_shared<SQLListUsers>(&this->db, this->getDBname(), cct);
dbops.InsertBucket = make_shared<SQLInsertBucket>(&this->db, this->getDBname(), cct);
dbops.UpdateBucket = make_shared<SQLUpdateBucket>(&this->db, this->getDBname(), cct);
dbops.RemoveBucket = make_shared<SQLRemoveBucket>(&this->db, this->getDBname(), cct);
return ret;
}
+int SQLListUsers::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLListUsers - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareListUsers");
+out:
+ return ret;
+}
+
+int SQLListUsers::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.list_max_count, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.list_max_count, sdb);
+out:
+ return rc;
+}
+
+int SQLListUsers::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, list_user);
+
+out:
+ return ret;
+}
+
int SQLInsertBucket::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
{
int ret = -1;
int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
};
+class SQLListUsers : public SQLiteDB, public ListUsersOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLListUsers(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLListUsers() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+
class SQLInsertBucket : public SQLiteDB, public InsertBucketOp {
private:
sqlite3 **sdb = NULL;
}
}
ldpp_dout(dpp, 20) << "root_fd: " << root_dir->get_fd() << dendl;
+ quota_handler = RGWQuotaHandler::generate_handler(dpp, this, true);
ldpp_dout(dpp, 20) << "SUCCESS" << dendl;
return 0;
}
+void POSIXDriver::finalize()
+{
+ RGWQuotaHandler::free_handler(quota_handler);
+}
+
std::unique_ptr<User> POSIXDriver::get_user(const rgw_user &u)
{
return std::make_unique<POSIXUser>(this, u);
errno = 0;
continue;
}
-
+ std::unique_ptr<Bucket> bucket;
+ ret = load_bucket(dpp, rgw_bucket("", entry->d_name), &bucket, null_yield);
+ if (bucket->get_owner() != owner) {
+ continue;
+ }
RGWBucketEnt ent;
ent.bucket.name = url_decode(entry->d_name);
ent.creation_time = ceph::real_clock::from_time_t(stx.stx_btime.tv_sec);
// TODO: ent.size and ent.count
result.buckets.push_back(std::move(ent));
-
errno = 0;
+ if (result.buckets.size() == max){
+ result.next_marker = ent.bucket.marker;
+ break;
+ }
}
ret = errno;
if (ret != 0) {
return std::unique_ptr<RGWRole>(p);
}
+struct meta_list_handle {
+ std::string marker;
+ std::string section;
+
+ DIR *dir = nullptr;
+ long dpos = -1;
+
+ meta_list_handle(const std::string& _section, const std::string& _marker) {
+ marker = _marker;
+ section = _section;
+ }
+};
+
+int POSIXDriver::meta_list_keys_init(const DoutPrefixProvider *dpp,
+ const std::string& section,
+ const std::string& marker, void** phandle)
+{
+ meta_list_handle* stuff = new meta_list_handle(section, marker);
+ *phandle = (void *)stuff;
+ if (section == "bucket") {
+ int ret;
+ int dfd = copy_dir_fd(get_root_fd());
+ if (dfd == -1) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open root to list buckets: "
+ << cpp_strerror(errno) << dendl;
+ return -ret;
+ }
+
+ stuff->dir = fdopendir(dfd);
+ if (stuff->dir == NULL) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open root to list buckets: "
+ << cpp_strerror(ret) << dendl;
+ ::close(dfd);
+ return -ret;
+ }
+ }
+ return 0;
+ }
+
+int POSIXDriver::meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle,
+ int max, std::list<std::string>& keys,
+ bool* truncated)
+{
+ meta_list_handle *h = static_cast<meta_list_handle *>(handle);
+ *truncated = false;
+ int ret;
+ keys.clear();
+ if (h->section == "user") {
+ ret = get_user_db()->list_users(dpp, h->marker, max, keys, truncated);
+ if (ret < 0) {
+ return ret;
+ }
+ if (keys.size() > 0) {
+ h->marker = *keys.rbegin();
+ if (std::cmp_equal(keys.size(),max)) {
+ *truncated = true;
+ }
+ }
+ } else if (h->section == "bucket") {
+ if (h->dpos != -1) {
+ seekdir(h->dir, h->dpos);
+ }
+ struct dirent* entry;
+ while ((entry = readdir(h->dir)) != NULL) {
+ if (entry->d_type == DT_UNKNOWN) {
+ struct statx stx;
+
+ ret = statx(get_root_fd(), entry->d_name, AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << entry->d_name << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ if (!S_ISDIR(stx.stx_mode)) {
+ /* Not a bucket, skip it */
+ continue;
+ }
+ } else if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ if (entry->d_name[0] == '.') {
+ /* Skip dotfiles */
+ continue;
+ }
+ keys.push_back(entry->d_name);
+ if (std::cmp_equal(keys.size(),max)) {
+ h->dpos = telldir(h->dir);
+ *truncated = true;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void POSIXDriver::meta_list_keys_complete(void* handle)
+{
+ if (handle) {
+ meta_list_handle *h = static_cast<meta_list_handle *>(handle);
+ if (h->section == "bucket") {
+ closedir(h->dir);
+ }
+ delete h;
+ }
+ return;
+}
+
int POSIXBucket::fill_cache(const DoutPrefixProvider* dpp, optional_yield y,
fill_cache_cb_t& cb)
{
int POSIXBucket::check_quota(const DoutPrefixProvider *dpp, RGWQuota& quota, uint64_t obj_size,
optional_yield y, bool check_size_only)
{
- return 0;
+ return driver->get_quota_handler()->check_quota(dpp, info.owner, get_key(),
+ quota, (check_size_only ? 0 : 1),
+ obj_size, y);
}
int POSIXBucket::try_refresh_info(const DoutPrefixProvider* dpp, ceph::real_time* pmtime, optional_yield y)
key.instance.clear();
driver->get_bucket_cache()->remove_entry(dpp, b->get_name(), key);
}
+ driver->get_quota_handler()->update_stats(b->get_owner(), b->get_key(),
+ -1, 0, state.accounted_size);
return 0;
}
uint32_t flags)
{
int ret;
+ uint64_t orig_size = 0;
+ auto exists = obj->check_exists(dpp);
+ if (exists) {
+ orig_size = obj->get_size();
+ }
if (if_match) {
if (strcmp(if_match, "*") == 0) {
// test the object is existing
- if (!obj->check_exists(dpp)) {
+ if (!exists) {
return -ERR_PRECONDITION_FAILED;
}
} else {
if (if_nomatch) {
if (strcmp(if_nomatch, "*") == 0) {
// test the object is not existing
- if (obj->check_exists(dpp)) {
+ if (!exists) {
return -ERR_PRECONDITION_FAILED;
}
} else {
return ret;
}
+ POSIXBucket *b = static_cast<POSIXBucket*>(obj->get_bucket());
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << obj->get_name() << dendl;
+ return -EINVAL;
+ }
+ driver->get_quota_handler()->update_stats(b->get_owner(), b->get_key(),
+ (exists ? 0 : 1), orig_size, accounted_size);
+
return 0;
}
#include "rgw_sal_filter.h"
#include "rgw_sal_store.h"
+#include "rgw_quota.h"
#include <cstdint>
#include <memory>
#include "common/dout.h"
std::unique_ptr<Directory> root_dir;
int root_fd;
RGWSyncModuleInstanceRef sync_module;
+ RGWQuotaHandler* quota_handler{nullptr};
public:
POSIXDriver(CephContext *_cct) : StoreDriver(), cct(_cct), zone(this)
std::map<rgw_user_bucket, rgw_usage_log_entry>& usage) override { return 0; }
virtual int trim_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, optional_yield y) override { return 0; }
virtual int get_config_key_val(std::string name, bufferlist* bl) override { return -ENOTSUP; }
- virtual int meta_list_keys_init(const DoutPrefixProvider *dpp, const std::string& section, const std::string& marker, void** phandle) override { return 0; }
- virtual int meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle, int max, std::list<std::string>& keys, bool* truncated) override { return 0; }
- virtual void meta_list_keys_complete(void* handle) override { return; }
+ virtual int meta_list_keys_init(const DoutPrefixProvider *dpp, const std::string& section, const std::string& marker, void** phandle) override;
+ virtual int meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle, int max, std::list<std::string>& keys, bool* truncated) override;
+ virtual void meta_list_keys_complete(void* handle) override;
virtual std::string meta_get_marker(void* handle) override { return ""; }
virtual int meta_remove(const DoutPrefixProvider* dpp, std::string& metadata_key, optional_yield y) override { return 0; }
virtual const RGWSyncModuleInstanceRef& get_sync_module() override { return sync_module; }
virtual const std::string& get_compression_type(const rgw_placement_rule& rule) override;
virtual bool valid_placement(const rgw_placement_rule& rule) override { return true; }
- virtual void finalize(void) override {}
+ virtual void finalize(void) override;
virtual CephContext* ctx(void) override { return userDB->ctx(); }
* by inotify or similar */
int mint_listing_entry(
const std::string& bucket, rgw_bucket_dir_entry& bde /* OUT */);
+
+ RGWQuotaHandler* get_quota_handler() {return quota_handler;}
};
class POSIXNotification : public StoreNotification {
}
}
}
-
+ quota_handler = RGWQuotaHandler::generate_handler(env->dpp, this, false);
/* ordered listing cache */
bucket_cache.reset(new BucketCache(
this, base_path, cache_base, 100, 3, 3, 3));