diff --git a/fsck/cmd/common.go b/fsck/cmd/common.go index bd5a28b00..b05ad5d5e 100644 --- a/fsck/cmd/common.go +++ b/fsck/cmd/common.go @@ -24,6 +24,7 @@ var ( InodesFile string DensFile string MetaPort string + InodeID uint64 ) var ( diff --git a/fsck/cmd/getInfo.go b/fsck/cmd/getInfo.go new file mode 100644 index 000000000..f7f58023d --- /dev/null +++ b/fsck/cmd/getInfo.go @@ -0,0 +1,170 @@ +// Copyright 2020 The CubeFS Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. + +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/cubefs/cubefs/proto" + "github.com/spf13/cobra" + "io/ioutil" + "net/http" + "strings" +) + +func newInfoCmd() *cobra.Command { + var c = &cobra.Command{ + Use: "get", + Short: "get inode's info", + Args: cobra.MinimumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + if err := getInodeInfo(); err != nil { + fmt.Println(err) + } + }, + } + + return c +} + +func getInodeInfo() (err error) { + mps, err := getMetaPartitions(MasterAddr, VolName) + if err != nil { + return err + } + + for _, mp := range mps { + if mp.Start <= InodeID && InodeID <= mp.End { + res, er := getExtentsByInode(InodeID, mp) + if er != nil { + return er + } + dumpInodeInfo(InodeID, mp, res) + break + } + } + return +} + +func getExtentsByInode(ino uint64, mp *proto.MetaPartitionView) (res *proto.GetExtentsResponse, err error) { + cmdline := fmt.Sprintf("http://%s:%s/getExtentsByInode?pid=%d&ino=%d", + strings.Split(mp.LeaderAddr, ":")[0], MetaPort, mp.PartitionID, ino) + resp, err := http.Get(cmdline) + if err != nil { + return nil, fmt.Errorf("Get request failed: %v %v", cmdline, err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("Invalid status code: %v", resp.StatusCode) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("ReadAll failed: %v", err) + } + + body := &struct { + Code int32 `json:"code"` + Msg string `json:"msg"` + Data json.RawMessage `json:"data"` + }{} + if err = json.Unmarshal(data, body); err != nil { + return nil, fmt.Errorf("Unmarshal failed: %v", err) + } + + if body.Code != http.StatusSeeOther { + return nil, fmt.Errorf("getExtentsByInode failed: code[%v] not %v", + body.Code, http.StatusSeeOther) + } + + if strings.Compare(body.Msg, "Ok") != 0 { + return nil, fmt.Errorf("getExtentsByInode failed: %v", body.Msg) + } + + if err = json.Unmarshal(body.Data, &res); err != nil { + return nil, fmt.Errorf("Unmarshal extents failed: %v", err) + } + return +} + +func dumpInodeInfo(ino uint64, mp *proto.MetaPartitionView, res *proto.GetExtentsResponse) { + fmt.Printf("Inode: %v\n", ino) + fmt.Printf("Generation: %v\n", res.Generation) + fmt.Printf("Size: %v\n", res.Size) + partitionIds := make([]uint64, 0) + if len(res.Extents) > 0 { + fmt.Printf("Extents:\n") + for i, et := range res.Extents { + fmt.Printf(" %v. FileOffset:%v, PartitionId:%v, ExtentId:%v, ExtentOffset:%v, Size:%v, CRC:%v\n", + i, et.FileOffset, et.PartitionId, et.ExtentId, et.ExtentOffset, et.Size, et.CRC) + partitionIds = append(partitionIds, et.PartitionId) + } + } + fmt.Printf("\nMetaPartition:\n") + fmt.Printf(" Id:%v\n", mp.PartitionID) + fmt.Printf(" Leader:%v\n", mp.LeaderAddr) + fmt.Printf(" Hosts:%v\n", mp.Members) + if len(partitionIds) > 0 { + dps, err := getDataPartitions(MasterAddr, VolName) + if err != nil { + return + } + fmt.Printf("DataPartitions:\n") + for i, id := range partitionIds { + for _, dp := range dps { + if dp.PartitionID == id { + fmt.Printf(" %v. Id:%v\n", i, id) + fmt.Printf(" Leader:%v\n", dp.LeaderAddr) + fmt.Printf(" Hosts:%v\n", dp.Hosts) + break + } + } + } + } +} + +func getDataPartitions(addr, name string) ([]*proto.DataPartitionResponse, error) { + resp, err := http.Get(fmt.Sprintf("http://%s%s?name=%s", addr, proto.ClientDataPartitions, name)) + if err != nil { + return nil, fmt.Errorf("Get data partitions failed: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("Invalid status code: %v", resp.StatusCode) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("Get data partitions read all body failed: %v", err) + } + + body := &struct { + Code int32 `json:"code"` + Msg string `json:"msg"` + Data json.RawMessage `json:"data"` + }{} + if err = json.Unmarshal(data, body); err != nil { + return nil, fmt.Errorf("Unmarshal data partitions body failed: %v", err) + } + + var dpv *proto.DataPartitionsView + if err = json.Unmarshal(body.Data, &dpv); err != nil { + return nil, fmt.Errorf("Unmarshal data partitions view failed: %v", err) + } + + return dpv.DataPartitions, nil +} diff --git a/fsck/cmd/root.go b/fsck/cmd/root.go index 26a2a9df5..7e160f2ae 100644 --- a/fsck/cmd/root.go +++ b/fsck/cmd/root.go @@ -40,6 +40,7 @@ func NewRootCmd() *cobra.Command { c.AddCommand( newCheckCmd(), newCleanCmd(), + newInfoCmd(), ) c.PersistentFlags().StringVarP(&MasterAddr, "master", "m", "", "master addresses") @@ -47,6 +48,7 @@ func NewRootCmd() *cobra.Command { c.PersistentFlags().StringVarP(&InodesFile, "inode-list", "i", "", "inode list file") c.PersistentFlags().StringVarP(&DensFile, "dentry-list", "d", "", "dentry list file") c.PersistentFlags().StringVarP(&MetaPort, "mport", "", "", "prof port of metanode") + c.PersistentFlags().Uint64VarP(&InodeID, "inode", "", 0, "inode id of a file") c.Flags().BoolVarP(&optShowVersion, "version", "v", false, "Show version information") return c } diff --git a/fsck/usage.md b/fsck/usage.md index 2d9e800ab..1e878987f 100644 --- a/fsck/usage.md +++ b/fsck/usage.md @@ -10,4 +10,5 @@ ./fsck clean inode --vol "" --inode-list "inodes.txt" --dentry-list "dens.txt" ./fsck clean dentry --master "127.0.0.1:17010" --vol "" --mport "17220" ./fsck clean dentry --vol "" --inode-list "inodes.txt" --dentry-list "dens.txt" +./fsck get --inode --master "127.0.0.1:17010" --vol "" --mport "17220" ```