diff --git a/src/control/cmd/daos/filesystem.go b/src/control/cmd/daos/filesystem.go index ccd013f00c4..b9b0823d5b7 100644 --- a/src/control/cmd/daos/filesystem.go +++ b/src/control/cmd/daos/filesystem.go @@ -21,10 +21,10 @@ import "C" import ( "fmt" + "github.com/pkg/errors" + "math" "strings" "unsafe" - - "github.com/pkg/errors" ) func dfsError(rc C.int) error { @@ -51,6 +51,7 @@ type fsCmd struct { DfuseEvict fsDfuseEvictCmd `command:"evict" description:"Evict object from dfuse"` Scan fsScanCmd `command:"scan" description:"Scan POSIX container and report statistics"` Chmod fsChmodCmd `command:"chmod" description:"Change file mode bits"` + Chown fsChownCmd `command:"chown" description:"changes the owner"` } type fsCopyCmd struct { @@ -694,3 +695,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 8478387325c..7dcad4e015f 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 a4588a21353..6120fb35bec 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 3f608b2fabf..d6cc4e43b5e 100644 --- a/src/utils/daos_dfs_hdlr.c +++ b/src/utils/daos_dfs_hdlr.c @@ -349,3 +349,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 3e6ac717eee..43282ddaee0 100644 --- a/src/utils/daos_hdlr.h +++ b/src/utils/daos_hdlr.h @@ -22,6 +22,7 @@ enum fs_op { FS_RESET_OCLASS, FS_CHECK, FS_CHMOD, + FS_CHOWN, }; enum cont_op { @@ -169,6 +170,8 @@ struct cmd_args_s { struct dfuse_mem_query dfuse_mem; /* --memquery */ struct dfuse_stat *dfuse_stat; mode_t object_mode; /* object mode bits */ + uid_t user_id; /* user id */ + gid_t group_id; /* group id */ }; int pool_autotest_hdlr(struct cmd_args_s *ap); @@ -205,6 +208,8 @@ 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