mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
1) update master inteface of datanode enable qos 2) master clean client info if long time no report 3) enable qos online shutdown and startup 4) master set client alloc preriod and count of girds hit trigger condition 5) datanode qos limit value set by master and add disable conf and http interface 6) client qos init buffer calculate according on real buffer storage 7) create limiter for both hot and cold volume Signed-off-by: leonrayang <chl696@sina.com>
1437 lines
33 KiB
Go
1437 lines
33 KiB
Go
// Copyright 2020 The ChubaoFS 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 main
|
|
|
|
/*
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
|
|
struct cfs_stat_info {
|
|
uint64_t ino;
|
|
uint64_t size;
|
|
uint64_t blocks;
|
|
uint64_t atime;
|
|
uint64_t mtime;
|
|
uint64_t ctime;
|
|
uint32_t atime_nsec;
|
|
uint32_t mtime_nsec;
|
|
uint32_t ctime_nsec;
|
|
mode_t mode;
|
|
uint32_t nlink;
|
|
uint32_t blk_size;
|
|
uint32_t uid;
|
|
uint32_t gid;
|
|
};
|
|
|
|
struct cfs_summary_info {
|
|
int64_t files;
|
|
int64_t subdirs;
|
|
int64_t fbytes;
|
|
};
|
|
|
|
struct cfs_dirent {
|
|
uint64_t ino;
|
|
char name[256];
|
|
char d_type;
|
|
uint32_t nameLen;
|
|
};
|
|
|
|
struct cfs_hdfs_stat_info {
|
|
uint64_t size;
|
|
uint64_t atime;
|
|
uint64_t mtime;
|
|
uint32_t atime_nsec;
|
|
uint32_t mtime_nsec;
|
|
mode_t mode;
|
|
};
|
|
|
|
struct cfs_dirent_info {
|
|
struct cfs_hdfs_stat_info stat;
|
|
char d_type;
|
|
char name[256];
|
|
uint32_t nameLen;
|
|
};
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/cubefs/cubefs/util/buf"
|
|
"io"
|
|
syslog "log"
|
|
"os"
|
|
gopath "path"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"github.com/cubefs/blobstore/api/access"
|
|
"github.com/cubefs/blobstore/common/trace"
|
|
|
|
"github.com/bits-and-blooms/bitset"
|
|
// "github.com/cubefs/blobstore/util/log"
|
|
"github.com/cubefs/cubefs/blockcache/bcache"
|
|
"github.com/cubefs/cubefs/client/fs"
|
|
"github.com/cubefs/cubefs/proto"
|
|
"github.com/cubefs/cubefs/sdk/data/blobstore"
|
|
"github.com/cubefs/cubefs/sdk/data/stream"
|
|
masterSDK "github.com/cubefs/cubefs/sdk/master"
|
|
"github.com/cubefs/cubefs/sdk/meta"
|
|
"github.com/cubefs/cubefs/util/errors"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
"github.com/cubefs/cubefs/util/stat"
|
|
"github.com/hashicorp/consul/api"
|
|
)
|
|
|
|
const (
|
|
defaultBlkSize = uint32(1) << 12
|
|
|
|
maxFdNum uint = 10240000
|
|
|
|
MaxSizePutOnce = int64(1) << 23
|
|
)
|
|
|
|
var gClientManager *clientManager
|
|
|
|
var (
|
|
statusOK = C.int(0)
|
|
// error status must be minus value
|
|
statusEIO = errorToStatus(syscall.EIO)
|
|
statusEINVAL = errorToStatus(syscall.EINVAL)
|
|
statusEEXIST = errorToStatus(syscall.EEXIST)
|
|
statusEBADFD = errorToStatus(syscall.EBADFD)
|
|
statusEACCES = errorToStatus(syscall.EACCES)
|
|
statusEMFILE = errorToStatus(syscall.EMFILE)
|
|
statusENOTDIR = errorToStatus(syscall.ENOTDIR)
|
|
statusEISDIR = errorToStatus(syscall.EISDIR)
|
|
)
|
|
var once sync.Once
|
|
|
|
func init() {
|
|
gClientManager = &clientManager{
|
|
clients: make(map[int64]*client),
|
|
}
|
|
}
|
|
|
|
func errorToStatus(err error) C.int {
|
|
if err == nil {
|
|
return 0
|
|
}
|
|
if errno, is := err.(syscall.Errno); is {
|
|
return -C.int(errno)
|
|
}
|
|
return -C.int(syscall.EIO)
|
|
}
|
|
|
|
type clientManager struct {
|
|
nextClientID int64
|
|
clients map[int64]*client
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type pushConfig struct {
|
|
PushAddr string `json:"pushAddr"`
|
|
}
|
|
|
|
func newClient() *client {
|
|
id := atomic.AddInt64(&gClientManager.nextClientID, 1)
|
|
c := &client{
|
|
id: id,
|
|
fdmap: make(map[uint]*file),
|
|
fdset: bitset.New(maxFdNum),
|
|
cwd: "/",
|
|
sc: fs.NewSummaryCache(fs.DefaultSummaryExpiration, fs.MaxSummaryCache),
|
|
ic: fs.NewInodeCache(fs.DefaultInodeExpiration, fs.MaxInodeCache),
|
|
dc: fs.NewDentryCache(),
|
|
}
|
|
|
|
gClientManager.mu.Lock()
|
|
gClientManager.clients[id] = c
|
|
gClientManager.mu.Unlock()
|
|
|
|
return c
|
|
}
|
|
|
|
func getClient(id int64) (c *client, exist bool) {
|
|
gClientManager.mu.RLock()
|
|
defer gClientManager.mu.RUnlock()
|
|
c, exist = gClientManager.clients[id]
|
|
return
|
|
}
|
|
|
|
func removeClient(id int64) {
|
|
gClientManager.mu.Lock()
|
|
defer gClientManager.mu.Unlock()
|
|
delete(gClientManager.clients, id)
|
|
}
|
|
|
|
type file struct {
|
|
fd uint
|
|
ino uint64
|
|
pino uint64
|
|
flags uint32
|
|
mode uint32
|
|
|
|
// dir only
|
|
dirp *dirStream
|
|
|
|
//rw
|
|
fileWriter *blobstore.Writer
|
|
fileReader *blobstore.Reader
|
|
}
|
|
|
|
type dirStream struct {
|
|
pos int
|
|
dirents []proto.Dentry
|
|
}
|
|
|
|
type client struct {
|
|
// client id allocated by libsdk
|
|
id int64
|
|
|
|
// mount config
|
|
volName string
|
|
masterAddr string
|
|
followerRead bool
|
|
logDir string
|
|
logLevel string
|
|
ebsEndpoint string
|
|
servicePath string
|
|
volType int
|
|
cacheAction int
|
|
ebsBlockSize int
|
|
enableBcache bool
|
|
readBlockThread int
|
|
writeBlockThread int
|
|
cacheRuleKey string
|
|
cacheThreshold int
|
|
enableSummary bool
|
|
secretKey string
|
|
accessKey string
|
|
subDir string
|
|
pushAddr string
|
|
cluster string
|
|
// runtime context
|
|
cwd string // current working directory
|
|
fdmap map[uint]*file
|
|
fdset *bitset.BitSet
|
|
fdlock sync.RWMutex
|
|
|
|
// server info
|
|
mw *meta.MetaWrapper
|
|
ec *stream.ExtentClient
|
|
ic *fs.InodeCache
|
|
dc *fs.DentryCache
|
|
bc *bcache.BcacheClient
|
|
ebsc *blobstore.BlobStoreClient
|
|
sc *fs.SummaryCache
|
|
}
|
|
|
|
//export cfs_new_client
|
|
func cfs_new_client() C.int64_t {
|
|
c := newClient()
|
|
// Just skip fd 0, 1, 2, to avoid confusion.
|
|
c.fdset.Set(0).Set(1).Set(2)
|
|
return C.int64_t(c.id)
|
|
}
|
|
|
|
//export cfs_set_client
|
|
func cfs_set_client(id C.int64_t, key, val *C.char) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
k := C.GoString(key)
|
|
v := C.GoString(val)
|
|
|
|
switch k {
|
|
case "volName":
|
|
c.volName = v
|
|
case "masterAddr":
|
|
c.masterAddr = v
|
|
case "followerRead":
|
|
if v == "true" {
|
|
c.followerRead = true
|
|
} else {
|
|
c.followerRead = false
|
|
}
|
|
case "logDir":
|
|
c.logDir = v
|
|
case "logLevel":
|
|
c.logLevel = v
|
|
case "enableBcache":
|
|
if v == "true" {
|
|
c.enableBcache = true
|
|
} else {
|
|
c.enableBcache = false
|
|
}
|
|
case "readBlockThread":
|
|
rt, err := strconv.Atoi(v)
|
|
if err == nil {
|
|
c.readBlockThread = rt
|
|
}
|
|
case "writeBlockThread":
|
|
wt, err := strconv.Atoi(v)
|
|
if err == nil {
|
|
c.writeBlockThread = wt
|
|
}
|
|
case "enableSummary":
|
|
if v == "true" {
|
|
c.enableSummary = true
|
|
} else {
|
|
c.enableSummary = false
|
|
}
|
|
case "accessKey":
|
|
c.accessKey = v
|
|
case "secretKey":
|
|
c.secretKey = v
|
|
case "pushAddr":
|
|
c.pushAddr = v
|
|
default:
|
|
return statusEINVAL
|
|
}
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_start_client
|
|
func cfs_start_client(id C.int64_t) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
err := c.start()
|
|
if err != nil {
|
|
syslog.Println(err)
|
|
return statusEIO
|
|
}
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_close_client
|
|
func cfs_close_client(id C.int64_t) {
|
|
if c, exist := getClient(int64(id)); exist {
|
|
if c.ec != nil {
|
|
_ = c.ec.Close()
|
|
}
|
|
if c.mw != nil {
|
|
_ = c.mw.Close()
|
|
}
|
|
removeClient(int64(id))
|
|
}
|
|
log.LogFlush()
|
|
}
|
|
|
|
//export cfs_chdir
|
|
func cfs_chdir(id C.int64_t, path *C.char) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
cwd := c.absPath(C.GoString(path))
|
|
dirInfo, err := c.lookupPath(cwd)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
if !proto.IsDir(dirInfo.Mode) {
|
|
return statusENOTDIR
|
|
}
|
|
c.cwd = cwd
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_getcwd
|
|
func cfs_getcwd(id C.int64_t) *C.char {
|
|
c, exist := getClient(int64(id)) // client's working directory
|
|
if !exist {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(c.cwd)
|
|
}
|
|
|
|
//export cfs_getattr
|
|
func cfs_getattr(id C.int64_t, path *C.char, stat *C.struct_cfs_stat_info) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
info, err := c.lookupPath(c.absPath(C.GoString(path)))
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
// fill up the stat
|
|
stat.ino = C.uint64_t(info.Inode)
|
|
stat.size = C.uint64_t(info.Size)
|
|
stat.nlink = C.uint32_t(info.Nlink)
|
|
stat.blk_size = C.uint32_t(defaultBlkSize)
|
|
stat.uid = C.uint32_t(info.Uid)
|
|
stat.gid = C.uint32_t(info.Gid)
|
|
|
|
if info.Size%512 != 0 {
|
|
stat.blocks = C.uint64_t(info.Size>>9) + 1
|
|
} else {
|
|
stat.blocks = C.uint64_t(info.Size >> 9)
|
|
}
|
|
// fill up the mode
|
|
if proto.IsRegular(info.Mode) {
|
|
stat.mode = C.uint32_t(C.S_IFREG) | C.uint32_t(info.Mode&0777)
|
|
} else if proto.IsDir(info.Mode) {
|
|
stat.mode = C.uint32_t(C.S_IFDIR) | C.uint32_t(info.Mode&0777)
|
|
} else if proto.IsSymlink(info.Mode) {
|
|
stat.mode = C.uint32_t(C.S_IFLNK) | C.uint32_t(info.Mode&0777)
|
|
} else {
|
|
stat.mode = C.uint32_t(C.S_IFSOCK) | C.uint32_t(info.Mode&0777)
|
|
}
|
|
|
|
// fill up the time struct
|
|
t := info.AccessTime.UnixNano()
|
|
stat.atime = C.uint64_t(t / 1e9)
|
|
stat.atime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
t = info.ModifyTime.UnixNano()
|
|
stat.mtime = C.uint64_t(t / 1e9)
|
|
stat.mtime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
t = info.CreateTime.UnixNano()
|
|
stat.ctime = C.uint64_t(t / 1e9)
|
|
stat.ctime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_setattr
|
|
func cfs_setattr(id C.int64_t, path *C.char, stat *C.struct_cfs_stat_info, valid C.int) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
info, err := c.lookupPath(c.absPath(C.GoString(path)))
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
err = c.setattr(info, uint32(valid), uint32(stat.mode), uint32(stat.uid), uint32(stat.gid), int64(stat.atime), int64(stat.mtime))
|
|
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
c.ic.Delete(info.Inode)
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_open
|
|
func cfs_open(id C.int64_t, path *C.char, flags C.int, mode C.mode_t) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
fuseMode := uint32(mode) & uint32(0777)
|
|
fuseFlags := uint32(flags) &^ uint32(0x8000)
|
|
accFlags := fuseFlags & uint32(C.O_ACCMODE)
|
|
|
|
absPath := c.absPath(C.GoString(path))
|
|
|
|
var info *proto.InodeInfo
|
|
var parentIno uint64
|
|
|
|
/*
|
|
* Note that the rwx mode is ignored when using libsdk
|
|
*/
|
|
|
|
if fuseFlags&uint32(C.O_CREAT) != 0 {
|
|
if accFlags != uint32(C.O_WRONLY) && accFlags != uint32(C.O_RDWR) {
|
|
return statusEACCES
|
|
}
|
|
dirpath, name := gopath.Split(absPath)
|
|
dirInfo, err := c.lookupPath(dirpath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
parentIno = dirInfo.Inode
|
|
newInfo, err := c.create(dirInfo.Inode, name, fuseMode)
|
|
if err != nil {
|
|
if err != syscall.EEXIST {
|
|
return errorToStatus(err)
|
|
}
|
|
newInfo, err = c.lookupPath(absPath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
}
|
|
info = newInfo
|
|
} else {
|
|
dirpath, _ := gopath.Split(absPath)
|
|
dirInfo, err := c.lookupPath(dirpath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
parentIno = dirInfo.Inode // parent inode
|
|
newInfo, err := c.lookupPath(absPath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
info = newInfo
|
|
}
|
|
var fileCache bool
|
|
if c.cacheRuleKey == "" {
|
|
fileCache = false
|
|
} else {
|
|
fileCachePattern := fmt.Sprintf(".*%s.*", c.cacheRuleKey)
|
|
fileCache, _ = regexp.MatchString(fileCachePattern, absPath)
|
|
}
|
|
f := c.allocFD(info.Inode, fuseFlags, fuseMode, fileCache, info.Size, parentIno)
|
|
if f == nil {
|
|
return statusEMFILE
|
|
}
|
|
|
|
if proto.IsRegular(info.Mode) {
|
|
c.openStream(f)
|
|
if fuseFlags&uint32(C.O_TRUNC) != 0 {
|
|
if accFlags != uint32(C.O_WRONLY) && accFlags != uint32(C.O_RDWR) {
|
|
c.closeStream(f)
|
|
c.releaseFD(f.fd)
|
|
return statusEACCES
|
|
}
|
|
if err := c.truncate(f, 0); err != nil {
|
|
c.closeStream(f)
|
|
c.releaseFD(f.fd)
|
|
return statusEIO
|
|
}
|
|
}
|
|
}
|
|
|
|
return C.int(f.fd)
|
|
}
|
|
|
|
//export cfs_flush
|
|
func cfs_flush(id C.int64_t, fd C.int) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return statusEBADFD
|
|
}
|
|
|
|
err := c.flush(f)
|
|
if err != nil {
|
|
return statusEIO
|
|
}
|
|
c.ic.Delete(f.ino)
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_close
|
|
func cfs_close(id C.int64_t, fd C.int) {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return
|
|
}
|
|
f := c.releaseFD(uint(fd))
|
|
if f != nil {
|
|
c.flush(f)
|
|
c.closeStream(f)
|
|
}
|
|
}
|
|
|
|
//export cfs_write
|
|
func cfs_write(id C.int64_t, fd C.int, buf unsafe.Pointer, size C.size_t, off C.off_t) C.ssize_t {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return C.ssize_t(statusEINVAL)
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return C.ssize_t(statusEBADFD)
|
|
}
|
|
|
|
accFlags := f.flags & uint32(C.O_ACCMODE)
|
|
if accFlags != uint32(C.O_WRONLY) && accFlags != uint32(C.O_RDWR) {
|
|
return C.ssize_t(statusEACCES)
|
|
}
|
|
|
|
var buffer []byte
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buffer))
|
|
hdr.Data = uintptr(buf)
|
|
hdr.Len = int(size)
|
|
hdr.Cap = int(size)
|
|
|
|
var flags int
|
|
var wait bool
|
|
|
|
if f.flags&uint32(C.O_DIRECT) != 0 || f.flags&uint32(C.O_SYNC) != 0 || f.flags&uint32(C.O_DSYNC) != 0 {
|
|
if proto.IsHot(c.volType) {
|
|
wait = true
|
|
}
|
|
}
|
|
if f.flags&uint32(C.O_APPEND) != 0 || proto.IsCold(c.volType) {
|
|
flags |= proto.FlagsAppend
|
|
flags |= proto.FlagsSyncWrite
|
|
}
|
|
|
|
n, err := c.write(f, int(off), buffer, flags)
|
|
if err != nil {
|
|
return C.ssize_t(statusEIO)
|
|
}
|
|
|
|
if wait {
|
|
if err = c.flush(f); err != nil {
|
|
return C.ssize_t(statusEIO)
|
|
}
|
|
}
|
|
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export cfs_read
|
|
func cfs_read(id C.int64_t, fd C.int, buf unsafe.Pointer, size C.size_t, off C.off_t) C.ssize_t {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return C.ssize_t(statusEINVAL)
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return C.ssize_t(statusEBADFD)
|
|
}
|
|
|
|
accFlags := f.flags & uint32(C.O_ACCMODE)
|
|
if accFlags == uint32(C.O_WRONLY) {
|
|
return C.ssize_t(statusEACCES)
|
|
}
|
|
|
|
var buffer []byte
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buffer))
|
|
hdr.Data = uintptr(buf)
|
|
hdr.Len = int(size)
|
|
hdr.Cap = int(size)
|
|
|
|
n, err := c.read(f, int(off), buffer)
|
|
if err != nil {
|
|
return C.ssize_t(statusEIO)
|
|
}
|
|
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export cfs_batch_get_inodes
|
|
func cfs_batch_get_inodes(id C.int64_t, fd C.int, iids unsafe.Pointer, stats []C.struct_cfs_stat_info, count C.int) (n C.int) {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return statusEBADFD
|
|
}
|
|
|
|
var inodeIDS []uint64
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&inodeIDS))
|
|
hdr.Data = uintptr(iids)
|
|
hdr.Len = int(count)
|
|
hdr.Cap = int(count)
|
|
|
|
infos := c.mw.BatchInodeGet(inodeIDS)
|
|
if len(infos) > int(count) {
|
|
return statusEINVAL
|
|
}
|
|
|
|
for i := 0; i < len(infos); i++ {
|
|
// fill up the stat
|
|
stats[i].ino = C.uint64_t(infos[i].Inode)
|
|
stats[i].size = C.uint64_t(infos[i].Size)
|
|
stats[i].blocks = C.uint64_t(infos[i].Size >> 9)
|
|
stats[i].nlink = C.uint32_t(infos[i].Nlink)
|
|
stats[i].blk_size = C.uint32_t(defaultBlkSize)
|
|
stats[i].uid = C.uint32_t(infos[i].Uid)
|
|
stats[i].gid = C.uint32_t(infos[i].Gid)
|
|
|
|
// fill up the mode
|
|
if proto.IsRegular(infos[i].Mode) {
|
|
stats[i].mode = C.uint32_t(C.S_IFREG) | C.uint32_t(infos[i].Mode&0777)
|
|
} else if proto.IsDir(infos[i].Mode) {
|
|
stats[i].mode = C.uint32_t(C.S_IFDIR) | C.uint32_t(infos[i].Mode&0777)
|
|
} else if proto.IsSymlink(infos[i].Mode) {
|
|
stats[i].mode = C.uint32_t(C.S_IFLNK) | C.uint32_t(infos[i].Mode&0777)
|
|
} else {
|
|
stats[i].mode = C.uint32_t(C.S_IFSOCK) | C.uint32_t(infos[i].Mode&0777)
|
|
}
|
|
|
|
// fill up the time struct
|
|
t := infos[i].AccessTime.UnixNano()
|
|
stats[i].atime = C.uint64_t(t / 1e9)
|
|
stats[i].atime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
t = infos[i].ModifyTime.UnixNano()
|
|
stats[i].mtime = C.uint64_t(t / 1e9)
|
|
stats[i].mtime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
t = infos[i].CreateTime.UnixNano()
|
|
stats[i].ctime = C.uint64_t(t / 1e9)
|
|
stats[i].ctime_nsec = C.uint32_t(t % 1e9)
|
|
}
|
|
|
|
n = C.int(len(infos))
|
|
return
|
|
}
|
|
|
|
//export cfs_refreshsummary
|
|
func cfs_refreshsummary(id C.int64_t, path *C.char, goroutine_num C.int) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
if !c.enableSummary {
|
|
return statusEINVAL
|
|
}
|
|
info, err := c.lookupPath(c.absPath(C.GoString(path)))
|
|
var ino uint64
|
|
if err != nil {
|
|
ino = proto.RootIno
|
|
} else {
|
|
ino = info.Inode
|
|
}
|
|
goroutineNum := int32(goroutine_num)
|
|
err = c.mw.RefreshSummary_ll(ino, goroutineNum)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
return statusOK
|
|
}
|
|
|
|
/*
|
|
* Note that readdir is not thread-safe according to the POSIX spec.
|
|
*/
|
|
|
|
//export cfs_readdir
|
|
func cfs_readdir(id C.int64_t, fd C.int, dirents []C.struct_cfs_dirent, count C.int) (n C.int) {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return statusEBADFD
|
|
}
|
|
|
|
if f.dirp == nil {
|
|
f.dirp = &dirStream{}
|
|
dentries, err := c.mw.ReadDir_ll(f.ino)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
f.dirp.dirents = dentries
|
|
}
|
|
|
|
dirp := f.dirp
|
|
for dirp.pos < len(dirp.dirents) && n < count {
|
|
// fill up ino
|
|
dirents[n].ino = C.uint64_t(dirp.dirents[dirp.pos].Inode)
|
|
|
|
// fill up d_type
|
|
if proto.IsRegular(dirp.dirents[dirp.pos].Type) {
|
|
dirents[n].d_type = C.DT_REG
|
|
} else if proto.IsDir(dirp.dirents[dirp.pos].Type) {
|
|
dirents[n].d_type = C.DT_DIR
|
|
} else if proto.IsSymlink(dirp.dirents[dirp.pos].Type) {
|
|
dirents[n].d_type = C.DT_LNK
|
|
} else {
|
|
dirents[n].d_type = C.DT_UNKNOWN
|
|
}
|
|
|
|
// fill up name
|
|
nameLen := len(dirp.dirents[dirp.pos].Name)
|
|
if nameLen >= 256 {
|
|
nameLen = 255
|
|
}
|
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&dirp.dirents[dirp.pos].Name))
|
|
C.memcpy(unsafe.Pointer(&dirents[n].name[0]), unsafe.Pointer(hdr.Data), C.size_t(nameLen))
|
|
dirents[n].name[nameLen] = 0
|
|
dirents[n].nameLen = C.uint32_t(nameLen)
|
|
// advance cursor
|
|
dirp.pos++
|
|
n++
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
//export cfs_lsdir
|
|
func cfs_lsdir(id C.int64_t, fd C.int, direntsInfo []C.struct_cfs_dirent_info, count C.int) (n C.int) {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return statusEBADFD
|
|
}
|
|
|
|
if f.dirp == nil {
|
|
f.dirp = &dirStream{}
|
|
dentries, err := c.mw.ReadDir_ll(f.ino)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
f.dirp.dirents = dentries
|
|
}
|
|
|
|
dirp := f.dirp
|
|
inodeIDS := make([]uint64, count, count)
|
|
inodeMap := make(map[uint64]C.int)
|
|
for dirp.pos < len(dirp.dirents) && n < count {
|
|
inodeIDS[n] = dirp.dirents[dirp.pos].Inode
|
|
inodeMap[dirp.dirents[dirp.pos].Inode] = n
|
|
// fill up d_type
|
|
if proto.IsRegular(dirp.dirents[dirp.pos].Type) {
|
|
direntsInfo[n].d_type = C.DT_REG
|
|
} else if proto.IsDir(dirp.dirents[dirp.pos].Type) {
|
|
direntsInfo[n].d_type = C.DT_DIR
|
|
} else if proto.IsSymlink(dirp.dirents[dirp.pos].Type) {
|
|
direntsInfo[n].d_type = C.DT_LNK
|
|
} else {
|
|
direntsInfo[n].d_type = C.DT_UNKNOWN
|
|
}
|
|
nameLen := len(dirp.dirents[dirp.pos].Name)
|
|
if nameLen >= 256 {
|
|
nameLen = 255
|
|
}
|
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&dirp.dirents[dirp.pos].Name))
|
|
|
|
C.memcpy(unsafe.Pointer(&direntsInfo[n].name[0]), unsafe.Pointer(hdr.Data), C.size_t(nameLen))
|
|
direntsInfo[n].name[nameLen] = 0
|
|
direntsInfo[n].nameLen = C.uint32_t(nameLen)
|
|
|
|
// advance cursor
|
|
dirp.pos++
|
|
n++
|
|
}
|
|
if n == 0 {
|
|
return n
|
|
}
|
|
infos := c.mw.BatchInodeGet(inodeIDS)
|
|
if len(infos) != int(n) {
|
|
return statusEIO
|
|
}
|
|
for i := 0; i < len(infos); i++ {
|
|
// fill up the stat
|
|
index := inodeMap[infos[i].Inode]
|
|
direntsInfo[index].stat.size = C.uint64_t(infos[i].Size)
|
|
|
|
// fill up the mode
|
|
if proto.IsRegular(infos[i].Mode) {
|
|
direntsInfo[index].stat.mode = C.uint32_t(C.S_IFREG) | C.uint32_t(infos[i].Mode&0777)
|
|
} else if proto.IsDir(infos[i].Mode) {
|
|
direntsInfo[index].stat.mode = C.uint32_t(C.S_IFDIR) | C.uint32_t(infos[i].Mode&0777)
|
|
} else if proto.IsSymlink(infos[i].Mode) {
|
|
direntsInfo[index].stat.mode = C.uint32_t(C.S_IFLNK) | C.uint32_t(infos[i].Mode&0777)
|
|
} else {
|
|
direntsInfo[index].stat.mode = C.uint32_t(C.S_IFSOCK) | C.uint32_t(infos[i].Mode&0777)
|
|
}
|
|
|
|
// fill up the time struct
|
|
t := infos[index].AccessTime.UnixNano()
|
|
direntsInfo[index].stat.atime = C.uint64_t(t / 1e9)
|
|
direntsInfo[index].stat.atime_nsec = C.uint32_t(t % 1e9)
|
|
|
|
t = infos[index].ModifyTime.UnixNano()
|
|
direntsInfo[index].stat.mtime = C.uint64_t(t / 1e9)
|
|
direntsInfo[index].stat.mtime_nsec = C.uint32_t(t % 1e9)
|
|
}
|
|
return n
|
|
|
|
}
|
|
|
|
//export cfs_mkdirs
|
|
func cfs_mkdirs(id C.int64_t, path *C.char, mode C.mode_t) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
dirpath := c.absPath(C.GoString(path))
|
|
if dirpath == "/" {
|
|
return statusEEXIST
|
|
}
|
|
|
|
pino := proto.RootIno
|
|
dirs := strings.Split(dirpath, "/")
|
|
for _, dir := range dirs {
|
|
if dir == "/" || dir == "" {
|
|
continue
|
|
}
|
|
child, _, err := c.mw.Lookup_ll(pino, dir)
|
|
if err != nil {
|
|
if err == syscall.ENOENT {
|
|
info, err := c.mkdir(pino, dir, uint32(mode))
|
|
|
|
if err != nil {
|
|
if err != syscall.EEXIST {
|
|
return errorToStatus(err)
|
|
}
|
|
} else {
|
|
child = info.Inode
|
|
}
|
|
} else {
|
|
return errorToStatus(err)
|
|
}
|
|
}
|
|
pino = child
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
//export cfs_rmdir
|
|
func cfs_rmdir(id C.int64_t, path *C.char) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
absPath := c.absPath(C.GoString(path))
|
|
dirpath, name := gopath.Split(absPath)
|
|
dirInfo, err := c.lookupPath(dirpath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
_, err = c.mw.Delete_ll(dirInfo.Inode, name, true)
|
|
c.ic.Delete(dirInfo.Inode)
|
|
c.dc.Delete(absPath)
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
//export cfs_unlink
|
|
func cfs_unlink(id C.int64_t, path *C.char) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
absPath := c.absPath(C.GoString(path))
|
|
dirpath, name := gopath.Split(absPath)
|
|
dirInfo, err := c.lookupPath(dirpath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
_, mode, err := c.mw.Lookup_ll(dirInfo.Inode, name)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
if proto.IsDir(mode) {
|
|
return statusEISDIR
|
|
}
|
|
|
|
info, err := c.mw.Delete_ll(dirInfo.Inode, name, false)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
if info != nil {
|
|
_ = c.mw.Evict(info.Inode)
|
|
c.ic.Delete(info.Inode)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
//export cfs_rename
|
|
func cfs_rename(id C.int64_t, from *C.char, to *C.char) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
absFrom := c.absPath(C.GoString(from))
|
|
absTo := c.absPath(C.GoString(to))
|
|
srcDirPath, srcName := gopath.Split(absFrom)
|
|
dstDirPath, dstName := gopath.Split(absTo)
|
|
|
|
srcDirInfo, err := c.lookupPath(srcDirPath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
dstDirInfo, err := c.lookupPath(dstDirPath)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
err = c.mw.Rename_ll(srcDirInfo.Inode, srcName, dstDirInfo.Inode, dstName, false)
|
|
c.ic.Delete(srcDirInfo.Inode)
|
|
c.ic.Delete(dstDirInfo.Inode)
|
|
c.dc.Delete(absFrom)
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
//export cfs_fchmod
|
|
func cfs_fchmod(id C.int64_t, fd C.int, mode C.mode_t) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
f := c.getFile(uint(fd))
|
|
if f == nil {
|
|
return statusEBADFD
|
|
}
|
|
|
|
info, err := c.mw.InodeGet_ll(f.ino)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
err = c.setattr(info, proto.AttrMode, uint32(mode), 0, 0, 0, 0)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
c.ic.Delete(info.Inode)
|
|
return statusOK
|
|
}
|
|
|
|
//export cfs_getsummary
|
|
func cfs_getsummary(id C.int64_t, path *C.char, summary *C.struct_cfs_summary_info, useCache *C.char, goroutine_num C.int) C.int {
|
|
c, exist := getClient(int64(id))
|
|
if !exist {
|
|
return statusEINVAL
|
|
}
|
|
|
|
info, err := c.lookupPath(c.absPath(C.GoString(path)))
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
|
|
if strings.ToLower(C.GoString(useCache)) == "true" {
|
|
cacheSummaryInfo := c.sc.Get(info.Inode)
|
|
if cacheSummaryInfo != nil {
|
|
summary.files = C.int64_t(cacheSummaryInfo.Files)
|
|
summary.subdirs = C.int64_t(cacheSummaryInfo.Subdirs)
|
|
summary.fbytes = C.int64_t(cacheSummaryInfo.Fbytes)
|
|
return statusOK
|
|
}
|
|
}
|
|
|
|
if !proto.IsDir(info.Mode) {
|
|
return statusENOTDIR
|
|
}
|
|
goroutineNum := int32(goroutine_num)
|
|
summaryInfo, err := c.mw.GetSummary_ll(info.Inode, goroutineNum)
|
|
if err != nil {
|
|
return errorToStatus(err)
|
|
}
|
|
if strings.ToLower(C.GoString(useCache)) != "false" {
|
|
c.sc.Put(info.Inode, &summaryInfo)
|
|
}
|
|
summary.files = C.int64_t(summaryInfo.Files)
|
|
summary.subdirs = C.int64_t(summaryInfo.Subdirs)
|
|
summary.fbytes = C.int64_t(summaryInfo.Fbytes)
|
|
return statusOK
|
|
}
|
|
|
|
// internals
|
|
|
|
func (c *client) absPath(path string) string {
|
|
p := gopath.Clean(path)
|
|
if !gopath.IsAbs(p) {
|
|
p = gopath.Join(c.cwd, p)
|
|
}
|
|
return gopath.Clean(p)
|
|
}
|
|
|
|
func (c *client) start() (err error) {
|
|
var masters = strings.Split(c.masterAddr, ",")
|
|
if c.logDir != "" {
|
|
if c.logLevel == "" {
|
|
c.logLevel = "WARN"
|
|
}
|
|
level := parseLogLevel(c.logLevel)
|
|
log.InitLog(c.logDir, "libcfs", level, nil)
|
|
stat.NewStatistic(c.logDir, "libcfs", int64(stat.DefaultStatLogSize), stat.DefaultTimeOutUs, true)
|
|
}
|
|
proto.InitBufferPool(int64(32768))
|
|
if c.readBlockThread == 0 {
|
|
c.readBlockThread = 10
|
|
}
|
|
if c.writeBlockThread == 0 {
|
|
c.writeBlockThread = 10
|
|
}
|
|
if err = c.loadConfFromMaster(masters); err != nil {
|
|
return
|
|
}
|
|
if err = c.checkPermission(); err != nil {
|
|
err = errors.NewErrorf("check permission failed: %v", err)
|
|
syslog.Println(err)
|
|
return
|
|
}
|
|
|
|
if c.enableSummary {
|
|
c.sc = fs.NewSummaryCache(fs.DefaultSummaryExpiration, fs.MaxSummaryCache)
|
|
}
|
|
if c.enableBcache {
|
|
c.bc = bcache.NewBcacheClient()
|
|
}
|
|
var ebsc *blobstore.BlobStoreClient
|
|
if c.ebsEndpoint != "" {
|
|
if ebsc, err = blobstore.NewEbsClient(access.Config{
|
|
ConnMode: access.NoLimitConnMode,
|
|
Consul: api.Config{
|
|
Address: c.ebsEndpoint,
|
|
},
|
|
MaxSizePutOnce: MaxSizePutOnce,
|
|
Logger: &access.Logger{
|
|
Filename: gopath.Join(c.logDir, "libcfs/ebs.log"),
|
|
},
|
|
}); err != nil {
|
|
return
|
|
}
|
|
}
|
|
var mw *meta.MetaWrapper
|
|
if mw, err = meta.NewMetaWrapper(&meta.MetaConfig{
|
|
Volume: c.volName,
|
|
Masters: masters,
|
|
ValidateOwner: false,
|
|
EnableSummary: c.enableSummary,
|
|
}); err != nil {
|
|
log.LogErrorf("newClient NewMetaWrapper failed(%v)", err)
|
|
return err
|
|
}
|
|
var ec *stream.ExtentClient
|
|
if ec, err = stream.NewExtentClient(&stream.ExtentConfig{
|
|
Volume: c.volName,
|
|
VolumeType: c.volType,
|
|
Masters: masters,
|
|
FollowerRead: c.followerRead,
|
|
OnAppendExtentKey: mw.AppendExtentKey,
|
|
OnGetExtents: mw.GetExtents,
|
|
OnTruncate: mw.Truncate,
|
|
BcacheEnable: c.enableBcache,
|
|
OnLoadBcache: c.bc.Get,
|
|
OnCacheBcache: c.bc.Put,
|
|
OnEvictBcache: c.bc.Evict,
|
|
}); err != nil {
|
|
log.LogErrorf("newClient NewExtentClient failed(%v)", err)
|
|
return
|
|
}
|
|
//if c.pushAddr == "" {
|
|
// c.pushAddr = "cfs-push.oppo.local"
|
|
//}
|
|
//pushCfg := pushConfig{
|
|
// PushAddr: c.pushAddr,
|
|
//}
|
|
//cfgJson, err := json.Marshal(pushCfg)
|
|
//if err != nil {
|
|
// log.LogErrorf("newClient NewExtentClient: marsh push addr fail.(%v)", err)
|
|
//}
|
|
//cfg := config.LoadConfigString(string(cfgJson))
|
|
//once.Do(func() {
|
|
// exporter.Init("hadoop-client", cfg)
|
|
// exporter.RegistConsul(c.cluster, "hadoop-client", cfg)
|
|
//})
|
|
|
|
c.mw = mw
|
|
c.ec = ec
|
|
c.ebsc = ebsc
|
|
return nil
|
|
}
|
|
|
|
func (c *client) checkPermission() (err error) {
|
|
if c.accessKey == "" || c.secretKey == "" {
|
|
err = errors.New("invalid AccessKey or SecretKey")
|
|
return
|
|
}
|
|
|
|
// checkPermission
|
|
var mc = masterSDK.NewMasterClientFromString(c.masterAddr, false)
|
|
var userInfo *proto.UserInfo
|
|
if userInfo, err = mc.UserAPI().GetAKInfo(c.accessKey); err != nil {
|
|
return
|
|
}
|
|
if userInfo.SecretKey != c.secretKey {
|
|
err = proto.ErrNoPermission
|
|
return
|
|
}
|
|
var policy = userInfo.Policy
|
|
if policy.IsOwn(c.volName) {
|
|
return
|
|
}
|
|
// read write
|
|
if policy.IsAuthorized(c.volName, c.subDir, proto.POSIXWriteAction) &&
|
|
policy.IsAuthorized(c.volName, c.subDir, proto.POSIXReadAction) {
|
|
return
|
|
}
|
|
// read only
|
|
if policy.IsAuthorized(c.volName, c.subDir, proto.POSIXReadAction) &&
|
|
!policy.IsAuthorized(c.volName, c.subDir, proto.POSIXWriteAction) {
|
|
return
|
|
}
|
|
err = proto.ErrNoPermission
|
|
return
|
|
}
|
|
|
|
func (c *client) allocFD(ino uint64, flags, mode uint32, fileCache bool, fileSize uint64, parentInode uint64) *file {
|
|
c.fdlock.Lock()
|
|
defer c.fdlock.Unlock()
|
|
fd, ok := c.fdset.NextClear(0)
|
|
if !ok || fd > maxFdNum {
|
|
return nil
|
|
}
|
|
c.fdset.Set(fd)
|
|
f := &file{fd: fd, ino: ino, flags: flags, mode: mode, pino: parentInode}
|
|
if proto.IsCold(c.volType) {
|
|
clientConf := blobstore.ClientConfig{
|
|
VolName: c.volName,
|
|
VolType: c.volType,
|
|
BlockSize: c.ebsBlockSize,
|
|
Ino: ino,
|
|
Bc: c.bc,
|
|
Mw: c.mw,
|
|
Ec: c.ec,
|
|
Ebsc: c.ebsc,
|
|
EnableBcache: c.enableBcache,
|
|
WConcurrency: c.writeBlockThread,
|
|
ReadConcurrency: c.readBlockThread,
|
|
CacheAction: c.cacheAction,
|
|
FileCache: fileCache,
|
|
FileSize: fileSize,
|
|
CacheThreshold: c.cacheThreshold,
|
|
}
|
|
|
|
switch flags & 0xff {
|
|
case syscall.O_RDONLY:
|
|
f.fileReader = blobstore.NewReader(clientConf)
|
|
case syscall.O_WRONLY:
|
|
f.fileWriter = blobstore.NewWriter(clientConf)
|
|
case syscall.O_RDWR:
|
|
f.fileReader = blobstore.NewReader(clientConf)
|
|
f.fileWriter = blobstore.NewWriter(clientConf)
|
|
default:
|
|
f.fileWriter = blobstore.NewWriter(clientConf)
|
|
}
|
|
}
|
|
c.fdmap[fd] = f
|
|
return f
|
|
}
|
|
|
|
func (c *client) getFile(fd uint) *file {
|
|
c.fdlock.Lock()
|
|
f := c.fdmap[fd]
|
|
c.fdlock.Unlock()
|
|
return f
|
|
}
|
|
|
|
func (c *client) releaseFD(fd uint) *file {
|
|
c.fdlock.Lock()
|
|
defer c.fdlock.Unlock()
|
|
f, ok := c.fdmap[fd]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
delete(c.fdmap, fd)
|
|
c.fdset.Clear(fd)
|
|
c.ic.Delete(f.ino)
|
|
return f
|
|
}
|
|
|
|
func (c *client) lookupPath(path string) (*proto.InodeInfo, error) {
|
|
ino, ok := c.dc.Get(gopath.Clean(path))
|
|
if !ok {
|
|
inoInterval, err := c.mw.LookupPath(gopath.Clean(path))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.dc.Put(gopath.Clean(path), inoInterval)
|
|
ino = inoInterval
|
|
}
|
|
info := c.ic.Get(ino)
|
|
if info != nil {
|
|
return info, nil
|
|
}
|
|
info, err := c.mw.InodeGet_ll(ino)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.ic.Put(info)
|
|
|
|
return info, nil
|
|
}
|
|
|
|
func (c *client) setattr(info *proto.InodeInfo, valid uint32, mode, uid, gid uint32, atime, mtime int64) error {
|
|
// Only rwx mode bit can be set
|
|
if valid&proto.AttrMode != 0 {
|
|
fuseMode := mode & uint32(0777)
|
|
mode = info.Mode &^ uint32(0777) // clear rwx mode bit
|
|
mode |= fuseMode
|
|
}
|
|
return c.mw.Setattr(info.Inode, valid, mode, uid, gid, atime, mtime)
|
|
}
|
|
|
|
func (c *client) create(pino uint64, name string, mode uint32) (info *proto.InodeInfo, err error) {
|
|
fuseMode := mode & 0777
|
|
return c.mw.Create_ll(pino, name, fuseMode, 0, 0, nil)
|
|
}
|
|
|
|
func (c *client) mkdir(pino uint64, name string, mode uint32) (info *proto.InodeInfo, err error) {
|
|
fuseMode := mode & 0777
|
|
fuseMode |= uint32(os.ModeDir)
|
|
return c.mw.Create_ll(pino, name, fuseMode, 0, 0, nil)
|
|
}
|
|
|
|
func (c *client) openStream(f *file) {
|
|
_ = c.ec.OpenStream(f.ino)
|
|
}
|
|
|
|
func (c *client) closeStream(f *file) {
|
|
_ = c.ec.CloseStream(f.ino)
|
|
_ = c.ec.EvictStream(f.ino)
|
|
f.fileWriter = nil
|
|
f.fileReader = nil
|
|
}
|
|
|
|
func (c *client) flush(f *file) error {
|
|
if proto.IsHot(c.volType) {
|
|
return c.ec.Flush(f.ino)
|
|
} else {
|
|
if f.fileWriter != nil {
|
|
return f.fileWriter.Flush(f.ino, c.ctx(c.id, f.ino))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *client) truncate(f *file, size int) error {
|
|
err := c.ec.Truncate(c.mw, f.pino, f.ino, size)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *client) write(f *file, offset int, data []byte, flags int) (n int, err error) {
|
|
if proto.IsHot(c.volType) {
|
|
c.ec.GetStreamer(f.ino).SetParentInode(f.pino) // set the parent inode
|
|
n, err = c.ec.Write(f.ino, offset, data, flags)
|
|
} else {
|
|
n, err = f.fileWriter.Write(c.ctx(c.id, f.ino), offset, data, flags)
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (c *client) read(f *file, offset int, data []byte) (n int, err error) {
|
|
if proto.IsHot(c.volType) {
|
|
n, err = c.ec.Read(f.ino, data, offset, len(data))
|
|
} else {
|
|
n, err = f.fileReader.Read(c.ctx(c.id, f.ino), data, offset, len(data))
|
|
}
|
|
if err != nil && err != io.EOF {
|
|
return 0, err
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (c *client) ctx(cid int64, ino uint64) context.Context {
|
|
_, ctx := trace.StartSpanFromContextWithTraceID(context.Background(), "", fmt.Sprintf("cid=%v,ino=%v", cid, ino))
|
|
return ctx
|
|
}
|
|
|
|
func (c *client) loadConfFromMaster(masters []string) (err error) {
|
|
mc := masterSDK.NewMasterClient(masters, false)
|
|
var volumeInfo *proto.SimpleVolView
|
|
volumeInfo, err = mc.AdminAPI().GetVolumeSimpleInfo(c.volName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
c.volType = volumeInfo.VolType
|
|
c.ebsBlockSize = volumeInfo.ObjBlockSize
|
|
c.cacheAction = volumeInfo.CacheAction
|
|
c.cacheRuleKey = volumeInfo.CacheRule
|
|
c.cacheThreshold = volumeInfo.CacheThreshold
|
|
|
|
var clusterInfo *proto.ClusterInfo
|
|
clusterInfo, err = mc.AdminAPI().GetClusterInfo()
|
|
if err != nil {
|
|
return
|
|
}
|
|
c.ebsEndpoint = clusterInfo.EbsAddr
|
|
c.servicePath = clusterInfo.ServicePath
|
|
c.cluster = clusterInfo.Cluster
|
|
buf.InitCachePool(c.ebsBlockSize)
|
|
return
|
|
}
|
|
|
|
func parseLogLevel(loglvl string) log.Level {
|
|
var level log.Level
|
|
switch strings.ToLower(loglvl) {
|
|
case "debug":
|
|
level = log.DebugLevel
|
|
case "info":
|
|
level = log.InfoLevel
|
|
case "warn":
|
|
level = log.WarnLevel
|
|
case "error":
|
|
level = log.ErrorLevel
|
|
default:
|
|
level = log.ErrorLevel
|
|
}
|
|
return level
|
|
}
|
|
|
|
func (c *client) fileSize(ino uint64) (size int, gen uint64) {
|
|
size, gen, valid := c.ec.FileSize(ino)
|
|
log.LogDebugf("fileSize: ino(%v) fileSize(%v) gen(%v) valid(%v)", ino, size, gen, valid)
|
|
|
|
if !valid {
|
|
info := c.ic.Get(ino)
|
|
if info != nil {
|
|
return int(info.Size), info.Generation
|
|
}
|
|
if info, err := c.mw.InodeGet_ll(ino); err == nil {
|
|
size = int(info.Size)
|
|
gen = info.Generation
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func main() {}
|