seaweedfs/weed/command/mount_std.go

289 lines
9.0 KiB
Go
Raw Normal View History

2022-02-15 05:48:48 +08:00
//go:build linux || darwin
// +build linux darwin
2022-02-11 12:32:13 +08:00
package command
import (
2022-02-11 14:43:55 +08:00
"context"
2022-02-11 12:32:13 +08:00
"fmt"
"net"
"net/http"
"os"
"os/user"
"runtime"
"strconv"
"strings"
"time"
2022-02-11 14:43:55 +08:00
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/mount"
"github.com/seaweedfs/seaweedfs/weed/mount/meta_cache"
"github.com/seaweedfs/seaweedfs/weed/mount/unmount"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/pb/mount_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
"github.com/seaweedfs/seaweedfs/weed/storage/types"
2022-04-03 06:14:37 +08:00
"google.golang.org/grpc/reflection"
2022-02-11 12:32:13 +08:00
"github.com/seaweedfs/seaweedfs/weed/util"
"github.com/seaweedfs/seaweedfs/weed/util/grace"
2022-02-11 12:32:13 +08:00
)
2022-02-27 18:57:27 +08:00
func runMount(cmd *Command, args []string) bool {
2022-02-11 12:32:13 +08:00
2022-02-27 18:57:27 +08:00
if *mountOptions.debug {
go http.ListenAndServe(fmt.Sprintf(":%d", *mountOptions.debugPort), nil)
2022-02-11 12:32:13 +08:00
}
grace.SetupProfiling(*mountCpuProfile, *mountMemProfile)
if *mountReadRetryTime < time.Second {
*mountReadRetryTime = time.Second
}
util.RetryWaitTime = *mountReadRetryTime
2022-02-27 18:57:27 +08:00
umask, umaskErr := strconv.ParseUint(*mountOptions.umaskString, 8, 64)
2022-02-11 12:32:13 +08:00
if umaskErr != nil {
2022-02-27 18:57:27 +08:00
fmt.Printf("can not parse umask %s", *mountOptions.umaskString)
2022-02-11 12:32:13 +08:00
return false
}
if len(args) > 0 {
return false
}
return RunMount(&mountOptions, os.FileMode(umask))
2022-02-11 12:32:13 +08:00
}
func RunMount(option *MountOptions, umask os.FileMode) bool {
2022-02-11 12:32:13 +08:00
2022-02-11 14:43:55 +08:00
// basic checks
2022-02-27 18:57:27 +08:00
chunkSizeLimitMB := *mountOptions.chunkSizeLimitMB
2022-02-11 14:43:55 +08:00
if chunkSizeLimitMB <= 0 {
fmt.Printf("Please specify a reasonable buffer size.")
return false
}
// try to connect to filer
filerAddresses := pb.ServerAddresses(*option.filer).ToAddresses()
2024-07-17 00:15:55 +08:00
util.LoadSecurityConfiguration()
2022-02-11 14:43:55 +08:00
grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
var cipher bool
var err error
for i := 0; i < 10; i++ {
err = pb.WithOneOfGrpcFilerClients(false, filerAddresses, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
if err != nil {
return fmt.Errorf("get filer grpc address %v configuration: %v", filerAddresses, err)
}
cipher = resp.Cipher
return nil
})
if err != nil {
glog.V(0).Infof("failed to talk to filer %v: %v", filerAddresses, err)
glog.V(0).Infof("wait for %d seconds ...", i+1)
time.Sleep(time.Duration(i+1) * time.Second)
}
}
if err != nil {
glog.Errorf("failed to talk to filer %v: %v", filerAddresses, err)
return true
}
filerMountRootPath := *option.filerMountRootPath
// clean up mount point
dir := util.ResolvePath(*option.dir)
if dir == "" {
fmt.Printf("Please specify the mount directory via \"-dir\"")
return false
}
unmount.Unmount(dir)
2022-04-03 06:14:37 +08:00
// start on local unix socket
if *option.localSocket == "" {
mountDirHash := util.HashToInt32([]byte(dir))
if mountDirHash < 0 {
mountDirHash = -mountDirHash
}
*option.localSocket = fmt.Sprintf("/tmp/seaweedfs-mount-%d.sock", mountDirHash)
2022-04-07 15:33:13 +08:00
}
if err := os.Remove(*option.localSocket); err != nil && !os.IsNotExist(err) {
glog.Fatalf("Failed to remove %s, error: %s", *option.localSocket, err.Error())
2022-04-03 06:14:37 +08:00
}
montSocketListener, err := net.Listen("unix", *option.localSocket)
if err != nil {
glog.Fatalf("Failed to listen on %s: %v", *option.localSocket, err)
}
2022-02-11 14:43:55 +08:00
// detect mount folder mode
if *option.dirAutoCreate {
os.MkdirAll(dir, os.FileMode(0777)&^umask)
}
fileInfo, err := os.Stat(dir)
// collect uid, gid
uid, gid := uint32(0), uint32(0)
mountMode := os.ModeDir | 0777
if err == nil {
mountMode = os.ModeDir | os.FileMode(0777)&^umask
uid, gid = util.GetFileUidGid(fileInfo)
fmt.Printf("mount point owner uid=%d gid=%d mode=%s\n", uid, gid, mountMode)
} else {
fmt.Printf("can not stat %s\n", dir)
return false
}
// detect uid, gid
if uid == 0 {
if u, err := user.Current(); err == nil {
if parsedId, pe := strconv.ParseUint(u.Uid, 10, 32); pe == nil {
uid = uint32(parsedId)
}
if parsedId, pe := strconv.ParseUint(u.Gid, 10, 32); pe == nil {
gid = uint32(parsedId)
}
fmt.Printf("current uid=%d gid=%d\n", uid, gid)
}
}
// mapping uid, gid
uidGidMapper, err := meta_cache.NewUidGidMapper(*option.uidMap, *option.gidMap)
if err != nil {
fmt.Printf("failed to parse %s %s: %v\n", *option.uidMap, *option.gidMap, err)
return false
}
// Ensure target mount point availability
if isValid := checkMountPointAvailable(dir); !isValid {
2022-09-01 14:16:05 +08:00
glog.Fatalf("Target mount point is not available: %s, please check!", dir)
2022-02-11 14:43:55 +08:00
return true
}
serverFriendlyName := strings.ReplaceAll(*option.filer, ",", "+")
2022-02-11 14:43:55 +08:00
// mount fuse
2022-02-12 13:35:09 +08:00
fuseMountOptions := &fuse.MountOptions{
AllowOther: *option.allowOthers,
Options: option.extraOptions,
2022-02-12 13:35:09 +08:00
MaxBackground: 128,
MaxWrite: 1024 * 1024 * 2,
MaxReadAhead: 1024 * 1024 * 2,
IgnoreSecurityLabels: false,
RememberInodes: false,
FsName: serverFriendlyName + ":" + filerMountRootPath,
2022-02-12 13:35:09 +08:00
Name: "seaweedfs",
SingleThreaded: false,
2022-06-06 11:27:12 +08:00
DisableXAttrs: *option.disableXAttr,
2022-02-27 18:02:30 +08:00
Debug: *option.debug,
2022-02-12 13:35:09 +08:00
EnableLocks: false,
ExplicitDataCacheControl: false,
2022-02-27 17:13:32 +08:00
DirectMount: true,
DirectMountFlags: 0,
//SyncRead: false, // set to false to enable the FUSE_CAP_ASYNC_READ capability
EnableAcl: true,
2022-02-27 17:13:32 +08:00
}
if *option.nonempty {
fuseMountOptions.Options = append(fuseMountOptions.Options, "nonempty")
}
if *option.readOnly {
if runtime.GOOS == "darwin" {
fuseMountOptions.Options = append(fuseMountOptions.Options, "rdonly")
} else {
fuseMountOptions.Options = append(fuseMountOptions.Options, "ro")
}
}
if runtime.GOOS == "darwin" {
// https://github-wiki-see.page/m/macfuse/macfuse/wiki/Mount-Options
ioSizeMB := 1
for ioSizeMB*2 <= *option.chunkSizeLimitMB && ioSizeMB*2 <= 32 {
ioSizeMB *= 2
}
fuseMountOptions.Options = append(fuseMountOptions.Options, "daemon_timeout=600")
if runtime.GOARCH == "amd64" {
fuseMountOptions.Options = append(fuseMountOptions.Options, "noapplexattr")
}
2022-02-27 18:02:30 +08:00
// fuseMountOptions.Options = append(fuseMountOptions.Options, "novncache") // need to test effectiveness
2022-02-27 17:13:32 +08:00
fuseMountOptions.Options = append(fuseMountOptions.Options, "slow_statfs")
fuseMountOptions.Options = append(fuseMountOptions.Options, "volname="+serverFriendlyName)
2022-02-27 17:13:32 +08:00
fuseMountOptions.Options = append(fuseMountOptions.Options, fmt.Sprintf("iosize=%d", ioSizeMB*1024*1024))
2022-02-12 13:35:09 +08:00
}
2022-02-11 12:46:53 +08:00
2022-02-11 14:43:55 +08:00
// find mount point
mountRoot := filerMountRootPath
if mountRoot != "/" && strings.HasSuffix(mountRoot, "/") {
mountRoot = mountRoot[0 : len(mountRoot)-1]
}
2024-09-04 15:05:58 +08:00
cacheDirForWrite := *option.cacheDirForWrite
if cacheDirForWrite == "" {
cacheDirForWrite = *option.cacheDirForRead
}
2022-02-11 14:43:55 +08:00
seaweedFileSystem := mount.NewSeaweedFileSystem(&mount.Option{
MountDirectory: dir,
FilerAddresses: filerAddresses,
GrpcDialOption: grpcDialOption,
FilerMountRootPath: mountRoot,
Collection: *option.collection,
Replication: *option.replication,
TtlSec: int32(*option.ttlSec),
DiskType: types.ToDiskType(*option.diskType),
ChunkSizeLimit: int64(chunkSizeLimitMB) * 1024 * 1024,
ConcurrentWriters: *option.concurrentWriters,
2023-08-17 14:47:43 +08:00
CacheDirForRead: *option.cacheDirForRead,
CacheSizeMBForRead: *option.cacheSizeMBForRead,
2024-09-04 15:05:58 +08:00
CacheDirForWrite: cacheDirForWrite,
CacheMetaTTlSec: *option.cacheMetaTtlSec,
2022-02-11 14:43:55 +08:00
DataCenter: *option.dataCenter,
Quota: int64(*option.collectionQuota) * 1024 * 1024,
2022-02-11 14:43:55 +08:00
MountUid: uid,
MountGid: gid,
MountMode: mountMode,
MountCtime: fileInfo.ModTime(),
MountMtime: time.Now(),
Umask: umask,
2022-02-27 18:57:27 +08:00
VolumeServerAccess: *mountOptions.volumeServerAccess,
2022-02-11 14:43:55 +08:00
Cipher: cipher,
UidGidMapper: uidGidMapper,
2022-06-06 11:27:12 +08:00
DisableXAttr: *option.disableXAttr,
IsMacOs: runtime.GOOS == "darwin",
2022-02-11 12:46:53 +08:00
})
// create mount root
mountRootPath := util.FullPath(mountRoot)
mountRootParent, mountDir := mountRootPath.DirAndName()
if err = filer_pb.Mkdir(seaweedFileSystem, mountRootParent, mountDir, nil); err != nil {
fmt.Printf("failed to create dir %s on filer %s: %v\n", mountRoot, filerAddresses, err)
return false
}
2022-02-12 13:35:09 +08:00
server, err := fuse.NewServer(seaweedFileSystem, dir, fuseMountOptions)
2022-02-11 12:32:13 +08:00
if err != nil {
glog.Fatalf("Mount fail: %v", err)
}
2022-02-11 14:43:55 +08:00
grace.OnInterrupt(func() {
unmount.Unmount(dir)
})
2022-04-03 06:14:37 +08:00
grpcS := pb.NewGrpcServer()
mount_pb.RegisterSeaweedMountServer(grpcS, seaweedFileSystem)
reflection.Register(grpcS)
go grpcS.Serve(montSocketListener)
err = seaweedFileSystem.StartBackgroundTasks()
if err != nil {
fmt.Printf("failed to start background tasks: %v\n", err)
return false
}
2022-02-14 17:09:31 +08:00
glog.V(0).Infof("mounted %s%s to %v", *option.filer, mountRoot, dir)
glog.V(0).Infof("This is SeaweedFS version %s %s %s", util.Version(), runtime.GOOS, runtime.GOARCH)
2022-02-11 14:43:55 +08:00
2022-02-12 13:35:09 +08:00
server.Serve()
2022-02-11 12:32:13 +08:00
return true
}