From 6fe95959423a654f4abbee874aab0d59cd068a09 Mon Sep 17 00:00:00 2001 From: Justin Zhang Date: Thu, 18 Apr 2024 02:04:51 +0000 Subject: [PATCH] b/334917514 - Add daos fs chown Example usage: https://paste.googleplex.com/5995835690582016 Required-githooks: true Signed-off-by: Justin Zhang --- src/control/cmd/daos/filesystem.go | 43 ++++++++++++++++ src/control/cmd/daos/flags.go | 48 ++++++++++++++++++ src/control/cmd/daos/flags_test.go | 80 ++++++++++++++++++++++++++++++ src/utils/daos_dfs_hdlr.c | 38 ++++++++++++++ src/utils/daos_hdlr.h | 4 ++ 5 files changed, 213 insertions(+) diff --git a/src/control/cmd/daos/filesystem.go b/src/control/cmd/daos/filesystem.go index b378a139c68a..2ec8065ce95a 100644 --- a/src/control/cmd/daos/filesystem.go +++ b/src/control/cmd/daos/filesystem.go @@ -13,6 +13,7 @@ import "C" import ( "fmt" + "math" "github.com/pkg/errors" ) @@ -38,6 +39,7 @@ type fsCmd struct { ResetChunkSize fsResetChunkSizeCmd `command:"reset-chunk-size" description:"reset fs chunk size"` ResetObjClass fsResetOclassCmd `command:"reset-oclass" description:"reset fs obj class"` Chmod fsChmodCmd `command:"chmod" description:"change file mode bits"` + Chown fsChownCmd `command:"chown" description:"changes the owner"` } type fsCopyCmd struct { @@ -461,3 +463,44 @@ func (cmd *fsChmodCmd) Execute(_ []string) error { return nil } + +type fsChownCmd struct { + fsAttrCmd + + UserId UserIdFlag `long:"user-id" short:"u" description:"user id"` + GroupId GroupIdFlag `long:"group-id" short:"g" description:"group id"` +} + +func (cmd *fsChownCmd) Execute(_ []string) error { + ap, deallocCmdArgs, err := setupFSAttrCmd(&cmd.fsAttrCmd) + if err != nil { + return err + } + defer deallocCmdArgs() + + if !cmd.UserId.Set && !cmd.GroupId.Set { + return errors.New("Missing --user-id and/or --group-id") + } + + ap.user_id = math.MaxUint32 + if cmd.UserId.Set { + ap.user_id = cmd.UserId.Id + } + + ap.group_id = math.MaxUint32 + if cmd.GroupId.Set { + ap.group_id = cmd.GroupId.Id + } + + cleanup, err := cmd.resolveAndConnect(C.DAOS_COO_RW, ap) + if err != nil { + return errors.Wrapf(err, "failed to connect") + } + defer cleanup() + + if err := dfsError(C.fs_chown_hdlr(ap)); err != nil { + return errors.Wrapf(err, "chown failed") + } + + return nil +} diff --git a/src/control/cmd/daos/flags.go b/src/control/cmd/daos/flags.go index a75c61f1c56f..d842960d90e3 100644 --- a/src/control/cmd/daos/flags.go +++ b/src/control/cmd/daos/flags.go @@ -300,3 +300,51 @@ func (f *ModeBitsFlag) UnmarshalFlag(fv string) error { return nil } + +type UserIdFlag struct { + Set bool + Id C.uid_t +} + +func (f *UserIdFlag) UnmarshalFlag(fv string) error { + if fv == "" { + return errors.New("empty user id flag") + } + + uid, err := strconv.ParseInt(fv, 10, 32) + if err != nil { + return errors.Errorf("invalid user id: %q", fv) + } + if uid < 0 { + return errors.Errorf("invalid user id: %q", fv) + } + + f.Set = true + f.Id = C.uid_t(uid) + + return nil +} + +type GroupIdFlag struct { + Set bool + Id C.gid_t +} + +func (f *GroupIdFlag) UnmarshalFlag(fv string) error { + if fv == "" { + return errors.New("empty group id flag") + } + + gid, err := strconv.ParseInt(fv, 10, 32) + if err != nil { + return errors.Errorf("invalid group id: %q", fv) + } + if gid < 0 { + return errors.Errorf("invalid group id: %q", fv) + } + + f.Set = true + f.Id = C.gid_t(gid) + + return nil +} diff --git a/src/control/cmd/daos/flags_test.go b/src/control/cmd/daos/flags_test.go index 5e0fbe52eae8..0879ca60be31 100644 --- a/src/control/cmd/daos/flags_test.go +++ b/src/control/cmd/daos/flags_test.go @@ -614,3 +614,83 @@ func TestFlags_ModeBitsFlag(t *testing.T) { } } + +func TestFlags_UserIdFlag(t *testing.T) { + for name, tc := range map[string]struct { + arg string + expFlag *UserIdFlag + expErr error + }{ + "unset": { + expErr: errors.New("empty user id flag"), + }, + "not an integer": { + arg: "foo", + expErr: errors.New("invalid user id: \"foo\""), + }, + "negative value": { + arg: "-1", + expErr: errors.New("invalid user id: \"-1\""), + }, + "valid": { + arg: "1000", + expFlag: &UserIdFlag{ + Set: true, + Id: 1000, + }, + }, + } { + t.Run(name, func(t *testing.T) { + f := UserIdFlag{} + gotErr := f.UnmarshalFlag(tc.arg) + test.CmpErr(t, tc.expErr, gotErr) + if tc.expErr != nil { + return + } + + if diff := cmp.Diff(tc.expFlag, &f); diff != "" { + t.Fatalf("unexpected flag value: (-want, +got)\n%s\n", diff) + } + }) + } +} + +func TestFlags_GroupIdFlag(t *testing.T) { + for name, tc := range map[string]struct { + arg string + expFlag *GroupIdFlag + expErr error + }{ + "unset": { + expErr: errors.New("empty group id flag"), + }, + "not an integer": { + arg: "foo", + expErr: errors.New("invalid group id: \"foo\""), + }, + "negative value": { + arg: "-1", + expErr: errors.New("invalid group id: \"-1\""), + }, + "valid": { + arg: "1000", + expFlag: &GroupIdFlag{ + Set: true, + Id: 1000, + }, + }, + } { + t.Run(name, func(t *testing.T) { + f := GroupIdFlag{} + gotErr := f.UnmarshalFlag(tc.arg) + test.CmpErr(t, tc.expErr, gotErr) + if tc.expErr != nil { + return + } + + if diff := cmp.Diff(tc.expFlag, &f); diff != "" { + t.Fatalf("unexpected flag value: (-want, +got)\n%s\n", diff) + } + }) + } +} diff --git a/src/utils/daos_dfs_hdlr.c b/src/utils/daos_dfs_hdlr.c index e3e928323fb6..9bc41f0fac9c 100644 --- a/src/utils/daos_dfs_hdlr.c +++ b/src/utils/daos_dfs_hdlr.c @@ -345,3 +345,41 @@ fs_chmod_hdlr(struct cmd_args_s *ap) fprintf(ap->errstream, "failed to umount DFS container\n"); return rc; } + +int +fs_chown_hdlr(struct cmd_args_s *ap) +{ + const int mflags = O_RDWR; + const int sflags = DFS_SYS_NO_LOCK | DFS_SYS_NO_CACHE; + dfs_sys_t *dfs_sys; + int rc = 0; + int rc2 = 0; + + rc = dfs_sys_mount(ap->pool, ap->cont, mflags, sflags, &dfs_sys); + if (rc) { + fprintf(ap->errstream, "failed to mount container %s: %s (%d)\n", + ap->cont_str, strerror(rc), rc); + return rc; + } + + if (ap->dfs_prefix) { + rc = dfs_sys_set_prefix(dfs_sys, ap->dfs_prefix); + if (rc) { + fprintf(ap->errstream, "failed to set path prefix %s: %s (%d)\n", + ap->dfs_prefix, strerror(rc), rc); + D_GOTO(out_umount, rc); + } + } + + rc = dfs_sys_chown(dfs_sys, ap->dfs_path, ap->user_id, ap->group_id, 0 /* flags */); + if (rc) { + fprintf(ap->errstream, "failed to change owner for path %s: %s (%d)\n", + ap->dfs_path, strerror(rc), rc); + } + +out_umount: + rc2 = dfs_sys_umount(dfs_sys); + if (rc2) + fprintf(ap->errstream, "failed to umount DFS container\n"); + return rc; +} diff --git a/src/utils/daos_hdlr.h b/src/utils/daos_hdlr.h index d36dc898b76e..8d1880f42ba1 100644 --- a/src/utils/daos_hdlr.h +++ b/src/utils/daos_hdlr.h @@ -20,6 +20,7 @@ enum fs_op { FS_RESET_OCLASS, FS_CHECK, FS_CHMOD, + FS_CHOWN, }; enum cont_op { @@ -163,6 +164,8 @@ struct cmd_args_s { char *entry; /* --entry for ACL */ char *principal; /* --principal for ACL */ mode_t object_mode; /* object mode bits */ + uid_t user_id; /* user id */ + gid_t group_id; /* group id */ }; #define ARGS_VERIFY_PATH_CREATE(ap, label, rcexpr) \ @@ -198,6 +201,7 @@ int fs_fix_entry_hdlr(struct cmd_args_s *ap, bool fix_entry); int fs_recreate_sb_hdlr(struct cmd_args_s *ap); int fs_relink_root_hdlr(struct cmd_args_s *ap); int fs_chmod_hdlr(struct cmd_args_s *ap); +int fs_chown_hdlr(struct cmd_args_s *ap); /* Container operations */ int cont_check_hdlr(struct cmd_args_s *ap);