Skip to content

Commit

Permalink
S3: add concept of bucket owner having (uid,gid) for filesystem opera…
Browse files Browse the repository at this point in the history
…tions and apply this concept whenever we are operating on the backend
  • Loading branch information
apeters1971 committed May 23, 2024
1 parent 41a4107 commit 50521a5
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 18 deletions.
8 changes: 4 additions & 4 deletions src/XrdS3/XrdS3Api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@ bool ParseCreateBucketBody(char* body, int length, std::string& location) {
}

//------------------------------------------------------------------------------
//! \brief Parse the XML body of a DeleteBucket request
//! \param body The body of the request
//! \param length The length of the body
//! \return true if the body is valid, false otherwise
//! \brief create a bucket handler
//! \param req given request
//! \return error code
//------------------------------------------------------------------------------
int S3Api::CreateBucketHandler(XrdS3Req& req) {
VALIDATE_REQUEST(Action::CreateBucket)
Expand Down Expand Up @@ -124,6 +123,7 @@ int S3Api::CreateBucketHandler(XrdS3Req& req) {
}

bucket.owner.id = req.id;
bucket.owner.resolve();
bucket.name = req.bucket;

RET_ON_ERROR(objectStore.CreateBucket(auth, bucket, location))
Expand Down
1 change: 1 addition & 0 deletions src/XrdS3/XrdS3Auth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ std::pair<S3Error, S3Auth::Bucket> S3Auth::GetBucket(
b.owner.id = S3Utils::GetXattr(path, "owner");
if (b.owner.id.empty()) return {S3Error::InternalError, b};

b.owner.resolve();
b.path = S3Utils::GetXattr(path, "path");
if (b.path.empty()) return {S3Error::InternalError, b};

Expand Down
17 changes: 16 additions & 1 deletion src/XrdS3/XrdS3Auth.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
#include <shared_mutex>
#include <string>
#include <vector>

#include <sys/types.h>
#include <pwd.h>
//------------------------------------------------------------------------------
#include "XrdS3ErrorResponse.hh"
#include "XrdS3Action.hh"
Expand Down Expand Up @@ -80,6 +81,20 @@ class S3Auth {
struct Owner {
std::string id;
std::string display_name;
uid_t uid;
gid_t gid;

void resolve() {
// translate username
struct passwd *pwd = getpwnam(id.c_str());
if (pwd == nullptr) {
uid=99;
gid=99;
} else {
uid = pwd->pw_uid;
gid = pwd->pw_gid;
}
}
};

struct Bucket {
Expand Down
66 changes: 54 additions & 12 deletions src/XrdS3/XrdS3ObjectStore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "XrdPosix/XrdPosixExtern.hh"
#include "XrdS3Auth.hh"
#include "XrdS3Req.hh"
#include "XrdS3ScopedFsId.hh"
//------------------------------------------------------------------------------

namespace S3 {
Expand Down Expand Up @@ -201,7 +202,13 @@ S3Error S3ObjectStore::CreateBucket(S3Auth &auth, S3Auth::Bucket bucket,
return S3Error::InternalError;
}

if (XrdPosix_Mkdir(bucket.path.c_str(), S_IRWXU | S_IRWXG)) {
int mkdir_retc=0;
{
// Create the backend directory with the users filesystem id
ScopedFsId(bucket.owner.uid,bucket.owner.gid);
mkdir_retc = XrdPosix_Mkdir(bucket.path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
}
if (mkdir_retc) {
auth.DeleteBucketInfo(bucket);
XrdPosix_Unlink(userInfoBucket.c_str());
XrdPosix_Rmdir((mtpu_path / bucket.name).c_str());
Expand All @@ -228,15 +235,22 @@ std::pair<std::string, std::string> BaseDir(std::string p) {
}

//------------------------------------------------------------------------------
//! DeleteBucket - Delete a bucket and all its contents
//! DeleteBucket - Delete a bucket and all its content
//! - we do this only it is empty and the backend bucket directory is not removed!
//! \param auth Authentication object
//! \param bucket Bucket to delete
//! \return S3Error::None if successful, S3Error::InternalError otherwise
//------------------------------------------------------------------------------
S3Error S3ObjectStore::DeleteBucket(S3Auth &auth,
const S3Auth::Bucket &bucket) {
if (!S3Utils::IsDirEmpty(bucket.path)) {
return S3Error::BucketNotEmpty;

{
// Check the backend directory with the users filesystem id
ScopedFsId(bucket.owner.uid,bucket.owner.gid);

if (!S3Utils::IsDirEmpty(bucket.path)) {
return S3Error::BucketNotEmpty;
}
}

auto upload_path = mtpu_path / bucket.name;
Expand Down Expand Up @@ -287,9 +301,12 @@ S3ObjectStore::Object::~Object() {
//! \param p Path to the object
//! \return S3Error::None if successful, S3Error::InternalError otherwise
//------------------------------------------------------------------------------
S3Error S3ObjectStore::Object::Init(const std::filesystem::path &p) {
S3Error S3ObjectStore::Object::Init(const std::filesystem::path &p,
uid_t uid, gid_t gid) {
struct stat buf;

// Do the backend operations with the users filesystem id
ScopedFsId(uid, gid);
if (XrdPosix_Stat(p.c_str(), &buf) || S_ISDIR(buf.st_mode)) {
return S3Error::NoSuchKey;
}
Expand Down Expand Up @@ -317,6 +334,10 @@ S3Error S3ObjectStore::Object::Init(const std::filesystem::path &p) {
this->buffer_size = std::min(this->size, MAX_BUFFSIZE);
this->last_modified = buf.st_mtim.tv_sec;

// store the ownership
this->uid = uid;
this->gid = gid;

return S3Error::None;
}

Expand Down Expand Up @@ -373,7 +394,7 @@ off_t S3ObjectStore::Object::Lseek(off_t offset, int whence) {
//------------------------------------------------------------------------------
S3Error S3ObjectStore::GetObject(const S3Auth::Bucket &bucket,
const std::string &object, Object &obj) {
return obj.Init(bucket.path / object);
return obj.Init(bucket.path / object, bucket.owner.uid, bucket.owner.gid);
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -1481,16 +1502,31 @@ S3Error S3ObjectStore::CompleteMultipartUpload(
}
}

if (!XrdPosix_Stat(final_path.c_str(), &buf) && S_ISDIR(buf.st_mode)) {
return S3Error::ObjectExistAsDir;
{
// Check if the final file exists in the backend and is a directory
ScopedFsId(bucket.owner.uid,bucket.owner.gid);

if (!XrdPosix_Stat(final_path.c_str(), &buf)) {
if (S_ISDIR(buf.st_mode)) {
return S3Error::ObjectExistAsDir;
}
} else {
return S3Error::AccessDenied;
}
}

// Then we copy all the parts into a tmp file, which will be renamed to the
// final file.
auto tmp_path = bucket.path /
("." + req.object + "." + std::to_string(std::time(nullptr)) +
std::to_string(std::rand()));
auto fd = XrdPosix_Open(tmp_path.c_str(), O_CREAT | O_EXCL | O_WRONLY);

int fd = 0;
{
// The temp file has to created using the filesystem id of the owner
ScopedFsId(bucket.owner.uid,bucket.owner.gid);
fd = XrdPosix_Open(tmp_path.c_str(), O_CREAT | O_EXCL | O_WRONLY);
}

if (fd < 0) {
return S3Error::InternalError;
Expand All @@ -1500,7 +1536,7 @@ S3Error S3ObjectStore::CompleteMultipartUpload(
xs.Init();

Object optimized_obj;
optimized_obj.Init(opt_path);
optimized_obj.Init(opt_path,bucket.owner.uid, bucket.owner.gid);

ssize_t opt_len;
try {
Expand All @@ -1514,7 +1550,7 @@ S3Error S3ObjectStore::CompleteMultipartUpload(
for (const auto &part : parts) {
Object obj;

if (obj.Init(upload_path / std::to_string(part.part_number)) !=
if (obj.Init(upload_path / std::to_string(part.part_number),bucket.owner.uid, bucket.owner.gid) !=
S3Error::None) {
// use the optimized part

Expand All @@ -1533,6 +1569,7 @@ S3Error S3ObjectStore::CompleteMultipartUpload(
ssize_t len = opt_len;
while ((i = optimized_obj.Read(len, &ptr)) > 0) {
if (len < i) {
ScopedFsId(bucket.owner.uid,bucket.owner.gid);
XrdPosix_Close(fd);
XrdPosix_Unlink(tmp_path.c_str());
S3Utils::RmPath(final_path.parent_path(), bucket.path);
Expand All @@ -1548,6 +1585,7 @@ S3Error S3ObjectStore::CompleteMultipartUpload(

while ((i = obj.Read(len, &ptr)) > 0) {
if (len < i) {
ScopedFsId(bucket.owner.uid,bucket.owner.gid);
XrdPosix_Close(fd);
XrdPosix_Unlink(tmp_path.c_str());
S3Utils::RmPath(final_path.parent_path(), bucket.path);
Expand Down Expand Up @@ -1576,7 +1614,11 @@ S3Error S3ObjectStore::CompleteMultipartUpload(
return error;
}

XrdPosix_Rename(tmp_path.c_str(), final_path.c_str());
{
// Rename using the owner filesystem id
ScopedFsId(bucket.owner.uid,bucket.owner.gid);
XrdPosix_Rename(tmp_path.c_str(), final_path.c_str());
}

XrdPosix_Unlink(opt_path.c_str());
DeleteMultipartUpload(bucket, key, upload_id);
Expand Down
4 changes: 3 additions & 1 deletion src/XrdS3/XrdS3ObjectStore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class S3ObjectStore {
Object() = default;
~Object();

S3Error Init(const std::filesystem::path &path);
S3Error Init(const std::filesystem::path &path, uid_t uid, gid_t gid);

[[nodiscard]] ssize_t GetSize() const { return size; };
[[nodiscard]] size_t BufferSize() const { return buffer_size; };
Expand All @@ -113,6 +113,8 @@ class S3ObjectStore {
size_t size{};
time_t last_modified{};
int fd{};
uid_t uid;
gid_t gid;
std::map<std::string, std::string> attributes{};
};

Expand Down

0 comments on commit 50521a5

Please sign in to comment.