diff --git a/README.md b/README.md index c7e6cddd6..43f59af75 100644 --- a/README.md +++ b/README.md @@ -93,13 +93,6 @@ and these volume servers manage files and their metadata. This relieves concurrency pressure from the central master and spreads file metadata into volume servers, allowing faster file access (O(1), usually just one disk read operation). -SeaweedFS can transparently integrate with the cloud. -With hot data on local cluster, and warm data on the cloud with O(1) access time, -SeaweedFS can achieve both fast local access time and elastic cloud storage capacity. -What's more, the cloud storage access API cost is minimized. -Faster and Cheaper than direct cloud storage! -Signup for future managed SeaweedFS cluster offering at "seaweedfilesystem at gmail dot com". - There is only 40 bytes of disk storage overhead for each file's metadata. It is so simple with O(1) disk reads that you are welcome to challenge the performance with your actual use cases. @@ -115,6 +108,12 @@ For any distributed key value stores, the large values can be offloaded to Seawe With the fast access speed and linearly scalable capacity, SeaweedFS can work as a distributed [Key-Large-Value store][KeyLargeValueStore]. +SeaweedFS can transparently integrate with the cloud. +With hot data on local cluster, and warm data on the cloud with O(1) access time, +SeaweedFS can achieve both fast local access time and elastic cloud storage capacity. +What's more, the cloud storage access API cost is minimized. +Faster and Cheaper than direct cloud storage! + [Back to TOC](#table-of-contents) ## Additional Features ## diff --git a/go.mod b/go.mod index bebe77d4e..4fccf19d2 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( cloud.google.com/go v0.58.0 // indirect cloud.google.com/go/pubsub v1.3.1 cloud.google.com/go/storage v1.9.0 - github.com/Azure/azure-amqp-common-go/v2 v2.1.0 // indirect github.com/Azure/azure-pipeline-go v0.2.2 // indirect github.com/Azure/azure-storage-blob-go v0.9.0 github.com/OneOfOne/xxhash v1.2.2 @@ -62,7 +61,7 @@ require ( github.com/pquerna/cachecontrol v0.1.0 github.com/prometheus/client_golang v1.3.0 github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 // indirect - github.com/seaweedfs/fuse v1.1.6 + github.com/seaweedfs/fuse v1.1.7 github.com/seaweedfs/goexif v1.0.2 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/go.sum b/go.sum index bf86e7912..4bff566eb 100644 --- a/go.sum +++ b/go.sum @@ -709,6 +709,8 @@ github.com/seaweedfs/fuse v1.1.5 h1:wyuRh/mDvrvt8ZLDS7YdPSe6nczniSx4sQFs/Jonveo= github.com/seaweedfs/fuse v1.1.5/go.mod h1:+PP6WlkrRUG6KPE+Th2EX5To/PjHaFsvqg/UgQ39aj8= github.com/seaweedfs/fuse v1.1.6 h1:kvCqaIsCEaYOBw5r8kJPUs9GcbwlIKcScnkPLT7HLuQ= github.com/seaweedfs/fuse v1.1.6/go.mod h1:+PP6WlkrRUG6KPE+Th2EX5To/PjHaFsvqg/UgQ39aj8= +github.com/seaweedfs/fuse v1.1.7 h1:T4L5c/Sn+q8lE+0zCmH2MWvIO+B5TttWOSqK5KQPRMQ= +github.com/seaweedfs/fuse v1.1.7/go.mod h1:+PP6WlkrRUG6KPE+Th2EX5To/PjHaFsvqg/UgQ39aj8= github.com/seaweedfs/goexif v1.0.2 h1:p+rTXYdQ2mgxd+1JaTrQ9N8DvYuw9UH9xgYmJ+Bb29E= github.com/seaweedfs/goexif v1.0.2/go.mod h1:MrKs5LK0HXdffrdCZrW3OIMegL2xXpC6ThLyXMyjdrk= github.com/secsy/goftp v0.0.0-20190720192957-f31499d7c79a h1:C6IhVTxNkhlb0tlCB6JfHOUv1f0xHPK7V8X4HlJZEJw= diff --git a/k8s/seaweedfs/Chart.yaml b/k8s/seaweedfs/Chart.yaml index f56e8ce1d..f7699fcb8 100644 --- a/k8s/seaweedfs/Chart.yaml +++ b/k8s/seaweedfs/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "2.49" -version: 2.49 +appVersion: "2.50" +version: "2.50" diff --git a/k8s/seaweedfs/values.yaml b/k8s/seaweedfs/values.yaml index 4b3b8e030..5ca4789ab 100644 --- a/k8s/seaweedfs/values.yaml +++ b/k8s/seaweedfs/values.yaml @@ -4,7 +4,7 @@ global: registry: "" repository: "" imageName: chrislusf/seaweedfs - # imageTag: "2.49" - started using {.Chart.appVersion} + # imageTag: "2.50" - started using {.Chart.appVersion} imagePullPolicy: IfNotPresent imagePullSecrets: imagepullsecret restartPolicy: Always diff --git a/note/SeaweedFS_Filer_Backup.png b/note/SeaweedFS_Filer_Backup.png new file mode 100644 index 000000000..62a29a430 Binary files /dev/null and b/note/SeaweedFS_Filer_Backup.png differ diff --git a/other/java/client/src/main/proto/filer.proto b/other/java/client/src/main/proto/filer.proto index ac4c9a0e7..cdbba7eb1 100644 --- a/other/java/client/src/main/proto/filer.proto +++ b/other/java/client/src/main/proto/filer.proto @@ -361,6 +361,7 @@ message FilerConf { string disk_type = 5; bool fsync = 6; uint32 volume_growth_count = 7; + bool read_only = 8; } repeated PathConf locations = 2; } diff --git a/weed/command/filer_sync.go b/weed/command/filer_sync.go index 0f34e5701..52fc0b477 100644 --- a/weed/command/filer_sync.go +++ b/weed/command/filer_sync.go @@ -359,16 +359,19 @@ func genProcessFunction(sourcePath string, targetPath string, dataSink sink.Repl return processEventFn } -func buildKey(dataSink sink.ReplicationSink, message *filer_pb.EventNotification, targetPath string, sourceKey util.FullPath, sourcePath string) string { +func buildKey(dataSink sink.ReplicationSink, message *filer_pb.EventNotification, targetPath string, sourceKey util.FullPath, sourcePath string) (key string) { if !dataSink.IsIncremental() { - return util.Join(targetPath, string(sourceKey)[len(sourcePath):]) - } - var mTime int64 - if message.NewEntry != nil { - mTime = message.NewEntry.Attributes.Mtime - } else if message.OldEntry != nil { - mTime = message.OldEntry.Attributes.Mtime + key = util.Join(targetPath, string(sourceKey)[len(sourcePath):]) + } else { + var mTime int64 + if message.NewEntry != nil { + mTime = message.NewEntry.Attributes.Mtime + } else if message.OldEntry != nil { + mTime = message.OldEntry.Attributes.Mtime + } + dateKey := time.Unix(mTime, 0).Format("2006-01-02") + key = util.Join(targetPath, dateKey, string(sourceKey)[len(sourcePath):]) } - dateKey := time.Unix(mTime, 0).Format("2006-01-02") - return util.Join(targetPath, dateKey, string(sourceKey)[len(sourcePath):]) + + return escapeKey(key) } diff --git a/weed/command/filer_sync_std.go b/weed/command/filer_sync_std.go new file mode 100644 index 000000000..63851eaf8 --- /dev/null +++ b/weed/command/filer_sync_std.go @@ -0,0 +1,7 @@ +// +build !windows + +package command + +func escapeKey(key string) string { + return key +} diff --git a/weed/command/filer_sync_windows.go b/weed/command/filer_sync_windows.go new file mode 100644 index 000000000..3d0c9146e --- /dev/null +++ b/weed/command/filer_sync_windows.go @@ -0,0 +1,12 @@ +package command + +import ( + "strings" +) + +func escapeKey(key string) string { + if strings.Contains(key, ":") { + return strings.ReplaceAll(key, ":", "") + } + return key +} diff --git a/weed/command/fuse.go b/weed/command/fuse.go index 0a55e509c..74cf2bb70 100644 --- a/weed/command/fuse.go +++ b/weed/command/fuse.go @@ -12,124 +12,178 @@ func init() { cmdFuse.Run = runFuse // break init cycle } -func runFuse(cmd *Command, args []string) bool { - argsLen := len(args) - options := []string{} +type parameter struct { + name string + value string +} - // at least target mount path should be passed - if argsLen < 1 { - return false +func runFuse(cmd *Command, args []string) bool { + rawArgs := strings.Join(args, " ") + rawArgsLen := len(rawArgs) + option := strings.Builder{} + options := []parameter{} + + // first parameter + i := 0 + for i = 0; i < rawArgsLen && rawArgs[i] != ' '; i++ { + option.WriteByte(rawArgs[i]) } + options = append(options, parameter{"arg0", option.String()}) + option.Reset() + + for i++; i < rawArgsLen; i++ { + + // space separator check for filled option + if rawArgs[i] == ' ' { + if option.Len() > 0 { + options = append(options, parameter{option.String(), "true"}) + option.Reset() + } + + // dash separator read option until next space + } else if rawArgs[i] == '-' { + for i++; i < rawArgsLen && rawArgs[i] != ' '; i++ { + option.WriteByte(rawArgs[i]) + } + options = append(options, parameter{option.String(), "true"}) + option.Reset() + + // equal separator start option with pending value + } else if rawArgs[i] == '=' { + name := option.String() + option.Reset() + + for i++; i < rawArgsLen && rawArgs[i] != ','; i++ { + // double quote separator read option until next double quote + if rawArgs[i] == '"' { + for i++; i < rawArgsLen && rawArgs[i] != '"'; i++ { + option.WriteByte(rawArgs[i]) + } + + // single quote separator read option until next single quote + } else if rawArgs[i] == '\'' { + for i++; i < rawArgsLen && rawArgs[i] != '\''; i++ { + option.WriteByte(rawArgs[i]) + } + + // add chars before comma + } else if rawArgs[i] != ' ' { + option.WriteByte(rawArgs[i]) + } + } - // first option is always target mount path - mountOptions.dir = &args[0] + options = append(options, parameter{name, option.String()}) + option.Reset() - // scan parameters looking for one or more -o options - // -o options receive parameters on format key=value[,key=value]... - for i := 0; i < argsLen; i++ { - if args[i] == "-o" && i+1 <= argsLen { - options = strings.Split(args[i+1], ",") - i++ + // comma separator just read current option + } else if rawArgs[i] == ',' { + options = append(options, parameter{option.String(), "true"}) + option.Reset() + + // what is not a separator fill option buffer + } else { + option.WriteByte(rawArgs[i]) } } - // for each option passed with -o - for _, option := range options { - // split just first = character - parts := strings.SplitN(option, "=", 2) - - // if doesn't key and value skip - if len(parts) != 2 { - continue - } + // get residual option data + if option.Len() > 0 { + // add value to pending option + options = append(options, parameter{option.String(), "true"}) + option.Reset() + } - key, value := parts[0], parts[1] - - // switch key keeping "weed mount" parameters - switch key { - case "filer": - mountOptions.filer = &value - case "filer.path": - mountOptions.filerMountRootPath = &value - case "dirAutoCreate": - if parsed, err := strconv.ParseBool(value); err != nil { - mountOptions.dirAutoCreate = &parsed - } else { - panic(fmt.Errorf("dirAutoCreate: %s", err)) - } - case "collection": - mountOptions.collection = &value - case "replication": - mountOptions.replication = &value - case "disk": - mountOptions.diskType = &value - case "ttl": - if parsed, err := strconv.ParseInt(value, 0, 32); err != nil { - intValue := int(parsed) - mountOptions.ttlSec = &intValue - } else { - panic(fmt.Errorf("ttl: %s", err)) - } - case "chunkSizeLimitMB": - if parsed, err := strconv.ParseInt(value, 0, 32); err != nil { - intValue := int(parsed) - mountOptions.chunkSizeLimitMB = &intValue - } else { - panic(fmt.Errorf("chunkSizeLimitMB: %s", err)) - } - case "concurrentWriters": - if parsed, err := strconv.ParseInt(value, 0, 32); err != nil { - intValue := int(parsed) - mountOptions.concurrentWriters = &intValue - } else { - panic(fmt.Errorf("concurrentWriters: %s", err)) - } - case "cacheDir": - mountOptions.cacheDir = &value - case "cacheCapacityMB": - if parsed, err := strconv.ParseInt(value, 0, 64); err != nil { - mountOptions.cacheSizeMB = &parsed - } else { - panic(fmt.Errorf("cacheCapacityMB: %s", err)) - } - case "dataCenter": - mountOptions.dataCenter = &value - case "allowOthers": - if parsed, err := strconv.ParseBool(value); err != nil { - mountOptions.allowOthers = &parsed - } else { - panic(fmt.Errorf("allowOthers: %s", err)) - } - case "umask": - mountOptions.umaskString = &value - case "nonempty": - if parsed, err := strconv.ParseBool(value); err != nil { - mountOptions.nonempty = &parsed - } else { - panic(fmt.Errorf("nonempty: %s", err)) - } - case "volumeServerAccess": - mountOptions.volumeServerAccess = &value - case "map.uid": - mountOptions.uidMap = &value - case "map.gid": - mountOptions.gidMap = &value - case "readOnly": - if parsed, err := strconv.ParseBool(value); err != nil { - mountOptions.readOnly = &parsed - } else { - panic(fmt.Errorf("readOnly: %s", err)) - } - case "cpuprofile": - mountCpuProfile = &value - case "memprofile": - mountMemProfile = &value - case "readRetryTime": - if parsed, err := time.ParseDuration(value); err != nil { - mountReadRetryTime = &parsed - } else { - panic(fmt.Errorf("readRetryTime: %s", err)) - } + // scan each parameter + for i := 0; i < len(options); i++ { + parameter := options[i] + + switch parameter.name { + case "arg0": + mountOptions.dir = ¶meter.value + case "filer": + mountOptions.filer = ¶meter.value + case "filer.path": + mountOptions.filerMountRootPath = ¶meter.value + case "dirAutoCreate": + if parsed, err := strconv.ParseBool(parameter.value); err != nil { + mountOptions.dirAutoCreate = &parsed + } else { + panic(fmt.Errorf("dirAutoCreate: %s", err)) + } + case "collection": + mountOptions.collection = ¶meter.value + case "replication": + mountOptions.replication = ¶meter.value + case "disk": + mountOptions.diskType = ¶meter.value + case "ttl": + if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil { + intValue := int(parsed) + mountOptions.ttlSec = &intValue + } else { + panic(fmt.Errorf("ttl: %s", err)) + } + case "chunkSizeLimitMB": + if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil { + intValue := int(parsed) + mountOptions.chunkSizeLimitMB = &intValue + } else { + panic(fmt.Errorf("chunkSizeLimitMB: %s", err)) + } + case "concurrentWriters": + i++ + if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil { + intValue := int(parsed) + mountOptions.concurrentWriters = &intValue + } else { + panic(fmt.Errorf("concurrentWriters: %s", err)) + } + case "cacheDir": + mountOptions.cacheDir = ¶meter.value + case "cacheCapacityMB": + if parsed, err := strconv.ParseInt(parameter.value, 0, 64); err != nil { + mountOptions.cacheSizeMB = &parsed + } else { + panic(fmt.Errorf("cacheCapacityMB: %s", err)) + } + case "dataCenter": + mountOptions.dataCenter = ¶meter.value + case "allowOthers": + if parsed, err := strconv.ParseBool(parameter.value); err != nil { + mountOptions.allowOthers = &parsed + } else { + panic(fmt.Errorf("allowOthers: %s", err)) + } + case "umask": + mountOptions.umaskString = ¶meter.value + case "nonempty": + if parsed, err := strconv.ParseBool(parameter.value); err != nil { + mountOptions.nonempty = &parsed + } else { + panic(fmt.Errorf("nonempty: %s", err)) + } + case "volumeServerAccess": + mountOptions.volumeServerAccess = ¶meter.value + case "map.uid": + mountOptions.uidMap = ¶meter.value + case "map.gid": + mountOptions.gidMap = ¶meter.value + case "readOnly": + if parsed, err := strconv.ParseBool(parameter.value); err != nil { + mountOptions.readOnly = &parsed + } else { + panic(fmt.Errorf("readOnly: %s", err)) + } + case "cpuprofile": + mountCpuProfile = ¶meter.value + case "memprofile": + mountMemProfile = ¶meter.value + case "readRetryTime": + if parsed, err := time.ParseDuration(parameter.value); err != nil { + mountReadRetryTime = &parsed + } else { + panic(fmt.Errorf("readRetryTime: %s", err)) + } } } @@ -160,6 +214,9 @@ var cmdFuse = &Command{ mount -t fuse./home/user/bin/weed fuse /mnt -o "filer=localhost:8888,filer.path=/" mount -t fuse "/home/user/bin/weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/" + To pass more than one parameter use quotes, example: + mount -t weed fuse /mnt -o "filer='192.168.0.1:8888,192.168.0.2:8888',filer.path=/" + To check valid options look "weed mount --help" `, } diff --git a/weed/command/mount_std.go b/weed/command/mount_std.go index e72a2f2cf..dce2197d6 100644 --- a/weed/command/mount_std.go +++ b/weed/command/mount_std.go @@ -5,15 +5,18 @@ package command import ( "context" "fmt" - "github.com/chrislusf/seaweedfs/weed/storage/types" "os" "os/user" "path" + "path/filepath" "runtime" "strconv" "strings" + "syscall" "time" + "github.com/chrislusf/seaweedfs/weed/storage/types" + "github.com/chrislusf/seaweedfs/weed/filesys/meta_cache" "github.com/seaweedfs/fuse" @@ -49,6 +52,21 @@ func runMount(cmd *Command, args []string) bool { return RunMount(&mountOptions, os.FileMode(umask)) } +func getParentInode(mountDir string) (uint64, error) { + parentDir := filepath.Clean(filepath.Join(mountDir, "..")) + fi, err := os.Stat(parentDir) + if err != nil { + return 0, err + } + + stat, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return 0, nil + } + + return stat.Ino, nil +} + func RunMount(option *MountOptions, umask os.FileMode) bool { filers := strings.Split(*option.filer, ",") @@ -85,13 +103,19 @@ func RunMount(option *MountOptions, umask os.FileMode) bool { filerMountRootPath := *option.filerMountRootPath dir := util.ResolvePath(*option.dir) - chunkSizeLimitMB := *mountOptions.chunkSizeLimitMB + parentInode, err := getParentInode(dir) + if err != nil { + glog.Errorf("failed to retrieve inode for parent directory of %s: %v", dir, err) + return true + } fmt.Printf("This is SeaweedFS version %s %s %s\n", util.Version(), runtime.GOOS, runtime.GOARCH) if dir == "" { fmt.Printf("Please specify the mount directory via \"-dir\"") return false } + + chunkSizeLimitMB := *mountOptions.chunkSizeLimitMB if chunkSizeLimitMB <= 0 { fmt.Printf("Please specify a reasonable buffer size.") return false @@ -199,6 +223,7 @@ func RunMount(option *MountOptions, umask os.FileMode) bool { MountMode: mountMode, MountCtime: fileInfo.ModTime(), MountMtime: time.Now(), + MountParentInode: parentInode, Umask: umask, VolumeServerAccess: *mountOptions.volumeServerAccess, Cipher: cipher, diff --git a/weed/command/scaffold.go b/weed/command/scaffold.go index 806e18fbc..2d6729bd3 100644 --- a/weed/command/scaffold.go +++ b/weed/command/scaffold.go @@ -356,13 +356,14 @@ sub_url = "rabbit://myqueue" REPLICATION_TOML_EXAMPLE = ` # A sample TOML config file for replicating SeaweedFS filer -# Used with "weed filer.replicate" +# Used with "weed filer.backup" +# Using with "weed filer.replicate" is deprecated. # Put this file to one of the location, with descending priority # ./replication.toml # $HOME/.seaweedfs/replication.toml # /etc/seaweedfs/replication.toml -[source.filer] +[source.filer] # deprecated. Only useful with "weed filer.replicate" enabled = true grpcAddress = "localhost:18888" # all files under this directory tree are replicated. diff --git a/weed/command/server.go b/weed/command/server.go index d0020d33b..d2bd6466e 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -245,7 +245,7 @@ func runServer(cmd *Command, args []string) bool { // start volume server if *isStartingVolumeServer { - minFreeSpaces := util.MustParseMinFreeSpace(*minFreeSpace, *minFreeSpacePercent) + minFreeSpaces := util.MustParseMinFreeSpace(*volumeMinFreeSpace, *volumeMinFreeSpacePercent) go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, minFreeSpaces) } diff --git a/weed/filer/filer_conf.go b/weed/filer/filer_conf.go index ab5afc5cc..da02c5b20 100644 --- a/weed/filer/filer_conf.go +++ b/weed/filer/filer_conf.go @@ -126,6 +126,9 @@ func mergePathConf(a, b *filer_pb.FilerConf_PathConf) { if b.VolumeGrowthCount > 0 { a.VolumeGrowthCount = b.VolumeGrowthCount } + if b.ReadOnly { + a.ReadOnly = b.ReadOnly + } } func (fc *FilerConf) ToProto() *filer_pb.FilerConf { diff --git a/weed/filer/filer_conf_test.go b/weed/filer/filer_conf_test.go index ff868a3ec..1576c7d82 100644 --- a/weed/filer/filer_conf_test.go +++ b/weed/filer/filer_conf_test.go @@ -24,6 +24,18 @@ func TestFilerConf(t *testing.T) { LocationPrefix: "/buckets/", Replication: "001", }, + { + LocationPrefix: "/buckets", + ReadOnly: false, + }, + { + LocationPrefix: "/buckets/xxx", + ReadOnly: true, + }, + { + LocationPrefix: "/buckets/xxx/yyy", + ReadOnly: false, + }, }} fc.doLoadConf(conf) @@ -31,4 +43,7 @@ func TestFilerConf(t *testing.T) { assert.Equal(t, "abcd", fc.MatchStorageRule("/buckets/abcd/jasdf").Collection) assert.Equal(t, "001", fc.MatchStorageRule("/buckets/abc/jasdf").Replication) + assert.Equal(t, true, fc.MatchStorageRule("/buckets/xxx/yyy/zzz").ReadOnly) + assert.Equal(t, false, fc.MatchStorageRule("/buckets/other").ReadOnly) + } diff --git a/weed/filesys/dir.go b/weed/filesys/dir.go index 72e41247f..904999c43 100644 --- a/weed/filesys/dir.go +++ b/weed/filesys/dir.go @@ -54,28 +54,27 @@ func (dir *Dir) Id() uint64 { func (dir *Dir) Attr(ctx context.Context, attr *fuse.Attr) error { - // https://github.com/bazil/fuse/issues/196 - attr.Valid = time.Second - - if dir.FullPath() == dir.wfs.option.FilerMountRootPath { - dir.setRootDirAttributes(attr) - glog.V(3).Infof("root dir Attr %s, attr: %+v", dir.FullPath(), attr) - return nil - } - entry, err := dir.maybeLoadEntry() if err != nil { - glog.V(3).Infof("dir Attr %s,err: %+v", dir.FullPath(), err) + glog.V(3).Infof("dir Attr %s, err: %+v", dir.FullPath(), err) return err } + // https://github.com/bazil/fuse/issues/196 + attr.Valid = time.Second attr.Inode = dir.Id() attr.Mode = os.FileMode(entry.Attributes.FileMode) | os.ModeDir attr.Mtime = time.Unix(entry.Attributes.Mtime, 0) attr.Crtime = time.Unix(entry.Attributes.Crtime, 0) + attr.Ctime = time.Unix(entry.Attributes.Crtime, 0) + attr.Atime = time.Unix(entry.Attributes.Mtime, 0) attr.Gid = entry.Attributes.Gid attr.Uid = entry.Attributes.Uid + if dir.FullPath() == dir.wfs.option.FilerMountRootPath { + attr.BlockSize = blockSize + } + glog.V(4).Infof("dir Attr %s, attr: %+v", dir.FullPath(), attr) return nil @@ -93,20 +92,6 @@ func (dir *Dir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *f return getxattr(entry, req, resp) } -func (dir *Dir) setRootDirAttributes(attr *fuse.Attr) { - // attr.Inode = 1 // filer2.FullPath(dir.Path).AsInode() - attr.Valid = time.Second - attr.Inode = dir.Id() - attr.Uid = dir.wfs.option.MountUid - attr.Gid = dir.wfs.option.MountGid - attr.Mode = dir.wfs.option.MountMode - attr.Crtime = dir.wfs.option.MountCtime - attr.Ctime = dir.wfs.option.MountCtime - attr.Mtime = dir.wfs.option.MountMtime - attr.Atime = dir.wfs.option.MountMtime - attr.BlockSize = blockSize -} - func (dir *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { // fsync works at OS level // write the file chunks to the filerGrpcAddress @@ -375,6 +360,28 @@ func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) { glog.Errorf("list meta cache: %v", listErr) return nil, fuse.EIO } + + // create proper . and .. directories + ret = append(ret, fuse.Dirent{ + Inode: dirPath.AsInode(), + Name: ".", + Type: fuse.DT_Dir, + }) + + // return the correct parent inode for the mount root + var inode uint64 + if string(dirPath) == dir.wfs.option.FilerMountRootPath { + inode = dir.wfs.option.MountParentInode + } else { + inode = util.FullPath(dir.parent.FullPath()).AsInode() + } + + ret = append(ret, fuse.Dirent{ + Inode: inode, + Name: "..", + Type: fuse.DT_Dir, + }) + return } diff --git a/weed/filesys/file.go b/weed/filesys/file.go index 4b711ecee..c50ac0549 100644 --- a/weed/filesys/file.go +++ b/weed/filesys/file.go @@ -115,16 +115,6 @@ func (file *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *f if err != nil { return err } - if file.isOpen > 0 { - file.wfs.handlesLock.Lock() - fileHandle := file.wfs.handles[file.Id()] - file.wfs.handlesLock.Unlock() - - if fileHandle != nil { - fileHandle.Lock() - defer fileHandle.Unlock() - } - } if req.Valid.Size() { @@ -207,6 +197,11 @@ func (file *File) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error if err := setxattr(entry, req); err != nil { return err } + file.dirtyMetadata = true + + if file.isOpen > 0 { + return nil + } return file.saveEntry(entry) @@ -224,6 +219,11 @@ func (file *File) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) if err := removexattr(entry, req); err != nil { return err } + file.dirtyMetadata = true + + if file.isOpen > 0 { + return nil + } return file.saveEntry(entry) @@ -351,6 +351,8 @@ func (file *File) saveEntry(entry *filer_pb.Entry) error { file.wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(request.Directory, request.Entry)) + file.dirtyMetadata = false + return nil }) } diff --git a/weed/filesys/meta_cache/meta_cache_subscribe.go b/weed/filesys/meta_cache/meta_cache_subscribe.go index f9973f436..747ac3cb9 100644 --- a/weed/filesys/meta_cache/meta_cache_subscribe.go +++ b/weed/filesys/meta_cache/meta_cache_subscribe.go @@ -40,9 +40,22 @@ func SubscribeMetaEvents(mc *MetaCache, selfSignature int32, client filer_pb.Fil newEntry = filer.FromPbEntry(dir, message.NewEntry) } err := mc.AtomicUpdateEntryFromFiler(context.Background(), oldPath, newEntry) - if err == nil && message.OldEntry != nil && message.NewEntry != nil { - key := util.NewFullPath(dir, message.NewEntry.Name) - mc.invalidateFunc(key) + if err == nil { + if message.OldEntry != nil && message.NewEntry != nil { + if message.OldEntry.Name == message.NewEntry.Name { + // no need to invalidate + } else { + oldKey := util.NewFullPath(resp.Directory, message.OldEntry.Name) + mc.invalidateFunc(oldKey) + newKey := util.NewFullPath(dir, message.NewEntry.Name) + mc.invalidateFunc(newKey) + } + } else if message.OldEntry == nil && message.NewEntry != nil { + // no need to invaalidate + } else if message.OldEntry != nil && message.NewEntry == nil { + oldKey := util.NewFullPath(resp.Directory, message.OldEntry.Name) + mc.invalidateFunc(oldKey) + } } return err diff --git a/weed/filesys/wfs.go b/weed/filesys/wfs.go index 8f864a123..178e4e497 100644 --- a/weed/filesys/wfs.go +++ b/weed/filesys/wfs.go @@ -3,9 +3,6 @@ package filesys import ( "context" "fmt" - "github.com/chrislusf/seaweedfs/weed/filer" - "github.com/chrislusf/seaweedfs/weed/storage/types" - "github.com/chrislusf/seaweedfs/weed/wdclient" "math" "math/rand" "os" @@ -14,6 +11,10 @@ import ( "sync" "time" + "github.com/chrislusf/seaweedfs/weed/filer" + "github.com/chrislusf/seaweedfs/weed/storage/types" + "github.com/chrislusf/seaweedfs/weed/wdclient" + "google.golang.org/grpc" "github.com/chrislusf/seaweedfs/weed/util/grace" @@ -46,11 +47,12 @@ type Option struct { DataCenter string Umask os.FileMode - MountUid uint32 - MountGid uint32 - MountMode os.FileMode - MountCtime time.Time - MountMtime time.Time + MountUid uint32 + MountGid uint32 + MountMode os.FileMode + MountCtime time.Time + MountMtime time.Time + MountParentInode uint64 VolumeServerAccess string // how to access volume servers Cipher bool // whether encrypt data on volume server diff --git a/weed/filesys/xattr.go b/weed/filesys/xattr.go index 92e43b675..473805116 100644 --- a/weed/filesys/xattr.go +++ b/weed/filesys/xattr.go @@ -113,6 +113,21 @@ func (wfs *WFS) maybeLoadEntry(dir, name string) (entry *filer_pb.Entry, err err fullpath := util.NewFullPath(dir, name) // glog.V(3).Infof("read entry cache miss %s", fullpath) + // return a valid entry for the mount root + if string(fullpath) == wfs.option.FilerMountRootPath { + return &filer_pb.Entry{ + Name: wfs.option.FilerMountRootPath, + IsDirectory: true, + Attributes: &filer_pb.FuseAttributes{ + Mtime: wfs.option.MountMtime.Unix(), + FileMode: uint32(wfs.option.MountMode), + Uid: wfs.option.MountUid, + Gid: wfs.option.MountGid, + Crtime: wfs.option.MountCtime.Unix(), + }, + }, nil + } + // read from async meta cache meta_cache.EnsureVisited(wfs.metaCache, wfs, util.FullPath(dir)) cachedEntry, cacheErr := wfs.metaCache.FindEntry(context.Background(), fullpath) diff --git a/weed/pb/filer.proto b/weed/pb/filer.proto index ac4c9a0e7..cdbba7eb1 100644 --- a/weed/pb/filer.proto +++ b/weed/pb/filer.proto @@ -361,6 +361,7 @@ message FilerConf { string disk_type = 5; bool fsync = 6; uint32 volume_growth_count = 7; + bool read_only = 8; } repeated PathConf locations = 2; } diff --git a/weed/pb/filer_pb/filer.pb.go b/weed/pb/filer_pb/filer.pb.go index 902c39514..89fc448f4 100644 --- a/weed/pb/filer_pb/filer.pb.go +++ b/weed/pb/filer_pb/filer.pb.go @@ -3135,6 +3135,7 @@ type FilerConf_PathConf struct { DiskType string `protobuf:"bytes,5,opt,name=disk_type,json=diskType,proto3" json:"disk_type,omitempty"` Fsync bool `protobuf:"varint,6,opt,name=fsync,proto3" json:"fsync,omitempty"` VolumeGrowthCount uint32 `protobuf:"varint,7,opt,name=volume_growth_count,json=volumeGrowthCount,proto3" json:"volume_growth_count,omitempty"` + ReadOnly bool `protobuf:"varint,8,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` } func (x *FilerConf_PathConf) Reset() { @@ -3218,6 +3219,13 @@ func (x *FilerConf_PathConf) GetVolumeGrowthCount() uint32 { return 0 } +func (x *FilerConf_PathConf) GetReadOnly() bool { + if x != nil { + return x.ReadOnly + } + return false +} + var File_filer_proto protoreflect.FileDescriptor var file_filer_proto_rawDesc = []byte{ @@ -3594,13 +3602,13 @@ var file_filer_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x25, 0x0a, 0x0d, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xce, 0x02, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x72, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xeb, 0x02, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x52, - 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0xea, 0x01, 0x0a, 0x08, 0x50, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x87, 0x02, 0x0a, 0x08, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, @@ -3615,114 +3623,116 @@ var file_filer_proto_rawDesc = []byte{ 0x52, 0x05, 0x66, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x2e, 0x0a, 0x13, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x47, 0x72, 0x6f, 0x77, - 0x74, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xdc, 0x0c, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x77, - 0x65, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x25, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x4c, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x4c, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, - 0x0d, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, - 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, - 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5e, 0x0a, 0x11, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x74, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, + 0x4f, 0x6e, 0x6c, 0x79, 0x32, 0xdc, 0x0c, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, + 0x46, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, + 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4c, + 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0b, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x41, 0x70, + 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, + 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x11, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x4f, 0x0a, 0x0c, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, - 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x12, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x12, 0x1b, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x6a, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x27, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, + 0x79, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, + 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, + 0x0c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, + 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x12, 0x1b, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x16, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, - 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x0d, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, - 0x0c, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, - 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, - 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, - 0x0a, 0x05, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, 0x76, - 0x50, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, - 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, 0x69, - 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4f, 0x0a, 0x10, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, - 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x46, 0x69, 0x6c, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, - 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x30, 0x01, 0x12, 0x56, 0x0a, 0x0d, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, + 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, + 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, + 0x76, 0x47, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, 0x76, 0x50, 0x75, 0x74, + 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, 0x75, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x4f, 0x0a, 0x10, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, + 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/weed/pb/grpc_client_server.go b/weed/pb/grpc_client_server.go index edb60e4fa..fac8adcb3 100644 --- a/weed/pb/grpc_client_server.go +++ b/weed/pb/grpc_client_server.go @@ -31,7 +31,8 @@ var ( type versionedGrpcClient struct { *grpc.ClientConn - version int + version int + errCount int } func init() { @@ -49,7 +50,7 @@ func NewGrpcServer(opts ...grpc.ServerOption) *grpc.Server { }), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ MinTime: 60 * time.Second, // min time a client should wait before sending a ping - PermitWithoutStream: false, + PermitWithoutStream: true, }), grpc.MaxRecvMsgSize(Max_Message_Size), grpc.MaxSendMsgSize(Max_Message_Size), @@ -75,7 +76,7 @@ func GrpcDial(ctx context.Context, address string, opts ...grpc.DialOption) (*gr grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: 30 * time.Second, // client ping server if no activity for this long Timeout: 20 * time.Second, - PermitWithoutStream: false, + PermitWithoutStream: true, })) for _, opt := range opts { if opt != nil { @@ -103,6 +104,7 @@ func getOrCreateConnection(address string, opts ...grpc.DialOption) (*versionedG vgc := &versionedGrpcClient{ grpcConnection, rand.Int(), + 0, } grpcClients[address] = vgc @@ -116,15 +118,20 @@ func WithCachedGrpcClient(fn func(*grpc.ClientConn) error, address string, opts return fmt.Errorf("getOrCreateConnection %s: %v", address, err) } executionErr := fn(vgc.ClientConn) - if executionErr != nil && strings.Contains(executionErr.Error(), "transport") { - grpcClientsLock.Lock() - if t, ok := grpcClients[address]; ok { - if t.version == vgc.version { - vgc.Close() - delete(grpcClients, address) + if executionErr != nil { + vgc.errCount++ + if vgc.errCount > 3 || + strings.Contains(executionErr.Error(), "transport") || + strings.Contains(executionErr.Error(), "connection closed") { + grpcClientsLock.Lock() + if t, ok := grpcClients[address]; ok { + if t.version == vgc.version { + vgc.Close() + delete(grpcClients, address) + } } + grpcClientsLock.Unlock() } - grpcClientsLock.Unlock() } return executionErr diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index e6140fd1a..17ead05a0 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -63,8 +63,8 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) } dataReader := r.Body + rAuthType := getRequestAuthType(r) if s3a.iam.isEnabled() { - rAuthType := getRequestAuthType(r) var s3ErrCode s3err.ErrorCode switch rAuthType { case authTypeStreamingSigned: @@ -79,8 +79,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) return } } else { - rAuthType := getRequestAuthType(r) - if authTypeAnonymous != rAuthType { + if authTypeStreamingSigned == rAuthType { writeErrorResponse(w, s3err.ErrAuthNotSetup, r.URL) return } diff --git a/weed/server/filer_grpc_server.go b/weed/server/filer_grpc_server.go index 3821de6a9..70f993962 100644 --- a/weed/server/filer_grpc_server.go +++ b/weed/server/filer_grpc_server.go @@ -259,14 +259,14 @@ func (fs *FilerServer) cleanupChunks(fullpath string, existingEntry *filer.Entry garbage = append(garbage, coveredChunks...) if newEntry.Attributes != nil { - so := fs.detectStorageOption(fullpath, + so, _ := fs.detectStorageOption(fullpath, newEntry.Attributes.Collection, newEntry.Attributes.Replication, newEntry.Attributes.TtlSec, newEntry.Attributes.DiskType, "", "", - ) + ) // ignore readonly error for capacity needed to manifestize chunks, err = filer.MaybeManifestize(fs.saveAsChunk(so), chunks) if err != nil { // not good, but should be ok @@ -307,7 +307,11 @@ func (fs *FilerServer) AppendToEntry(ctx context.Context, req *filer_pb.AppendTo } entry.Chunks = append(entry.Chunks, req.Chunks...) - so := fs.detectStorageOption(string(fullpath), entry.Collection, entry.Replication, entry.TtlSec, entry.DiskType, "", "") + so, err := fs.detectStorageOption(string(fullpath), entry.Collection, entry.Replication, entry.TtlSec, entry.DiskType, "", "") + if err != nil { + glog.Warningf("detectStorageOption: %v", err) + return &filer_pb.AppendToEntryResponse{}, err + } entry.Chunks, err = filer.MaybeManifestize(fs.saveAsChunk(so), entry.Chunks) if err != nil { // not good, but should be ok @@ -333,7 +337,11 @@ func (fs *FilerServer) DeleteEntry(ctx context.Context, req *filer_pb.DeleteEntr func (fs *FilerServer) AssignVolume(ctx context.Context, req *filer_pb.AssignVolumeRequest) (resp *filer_pb.AssignVolumeResponse, err error) { - so := fs.detectStorageOption(req.Path, req.Collection, req.Replication, req.TtlSec, req.DiskType, req.DataCenter, req.Rack) + so, err := fs.detectStorageOption(req.Path, req.Collection, req.Replication, req.TtlSec, req.DiskType, req.DataCenter, req.Rack) + if err != nil { + glog.V(3).Infof("AssignVolume: %v", err) + return &filer_pb.AssignVolumeResponse{Error: fmt.Sprintf("assign volume: %v", err)}, nil + } assignRequest, altRequest := so.ToAssignRequests(int(req.Count)) diff --git a/weed/server/filer_server_handlers_write.go b/weed/server/filer_server_handlers_write.go index 95eba9d3d..0567143fe 100644 --- a/weed/server/filer_server_handlers_write.go +++ b/weed/server/filer_server_handlers_write.go @@ -2,6 +2,7 @@ package weed_server import ( "context" + "errors" "net/http" "os" "strings" @@ -19,6 +20,8 @@ import ( var ( OS_UID = uint32(os.Getuid()) OS_GID = uint32(os.Getgid()) + + ErrReadOnly = errors.New("read only") ) type FilerPostResult struct { @@ -57,7 +60,7 @@ func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request, conte ctx := context.Background() query := r.URL.Query() - so := fs.detectStorageOption0(r.RequestURI, + so, err := fs.detectStorageOption0(r.RequestURI, query.Get("collection"), query.Get("replication"), query.Get("ttl"), @@ -65,6 +68,15 @@ func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request, conte query.Get("dataCenter"), query.Get("rack"), ) + if err != nil { + if err == ErrReadOnly { + w.WriteHeader(http.StatusInsufficientStorage) + } else { + glog.V(1).Infoln("post", r.RequestURI, ":", err.Error()) + w.WriteHeader(http.StatusInternalServerError) + } + return + } fs.autoChunk(ctx, w, r, contentLength, so) util.CloseRequest(r) @@ -105,7 +117,7 @@ func (fs *FilerServer) DeleteHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } -func (fs *FilerServer) detectStorageOption(requestURI, qCollection, qReplication string, ttlSeconds int32, diskType string, dataCenter, rack string) *operation.StorageOption { +func (fs *FilerServer) detectStorageOption(requestURI, qCollection, qReplication string, ttlSeconds int32, diskType, dataCenter, rack string) (*operation.StorageOption, error) { collection := util.Nvl(qCollection, fs.option.Collection) replication := util.Nvl(qReplication, fs.option.DefaultReplication) @@ -121,6 +133,10 @@ func (fs *FilerServer) detectStorageOption(requestURI, qCollection, qReplication rule := fs.filer.FilerConf.MatchStorageRule(requestURI) + if rule.ReadOnly { + return nil, ErrReadOnly + } + if ttlSeconds == 0 { ttl, err := needle.ReadTTL(rule.GetTtl()) if err != nil { @@ -138,10 +154,10 @@ func (fs *FilerServer) detectStorageOption(requestURI, qCollection, qReplication DiskType: util.Nvl(diskType, rule.DiskType), Fsync: fsync || rule.Fsync, VolumeGrowthCount: rule.VolumeGrowthCount, - } + }, nil } -func (fs *FilerServer) detectStorageOption0(requestURI, qCollection, qReplication string, qTtl string, diskType string, dataCenter, rack string) *operation.StorageOption { +func (fs *FilerServer) detectStorageOption0(requestURI, qCollection, qReplication string, qTtl string, diskType string, dataCenter, rack string) (*operation.StorageOption, error) { ttl, err := needle.ReadTTL(qTtl) if err != nil { diff --git a/weed/server/filer_server_handlers_write_autochunk.go b/weed/server/filer_server_handlers_write_autochunk.go index c43922ea9..fcb92d8ec 100644 --- a/weed/server/filer_server_handlers_write_autochunk.go +++ b/weed/server/filer_server_handlers_write_autochunk.go @@ -218,7 +218,7 @@ func (fs *FilerServer) saveMetaData(ctx context.Context, r *http.Request, fileNa entry.Extended = make(map[string][]byte) } - SaveAmzMetaData(r, entry.Extended, false) + entry.Extended = SaveAmzMetaData(r, entry.Extended, false) for k, v := range r.Header { if len(v) > 0 && (strings.HasPrefix(k, needle.PairNamePrefix) || k == "Cache-Control" || k == "Expires") { diff --git a/weed/shell/command_fs_configure.go b/weed/shell/command_fs_configure.go index 02cd7ac69..52fcae1c6 100644 --- a/weed/shell/command_fs_configure.go +++ b/weed/shell/command_fs_configure.go @@ -54,6 +54,7 @@ func (c *commandFsConfigure) Do(args []string, commandEnv *CommandEnv, writer io ttl := fsConfigureCommand.String("ttl", "", "assign writes with this ttl") diskType := fsConfigureCommand.String("disk", "", "[hdd|ssd|] hard drive or solid state drive or any tag") fsync := fsConfigureCommand.Bool("fsync", false, "fsync for the writes") + isReadOnly := fsConfigureCommand.Bool("readOnly", false, "disable writes") volumeGrowthCount := fsConfigureCommand.Int("volumeGrowthCount", 0, "the number of physical volumes to add if no writable volumes") isDelete := fsConfigureCommand.Bool("delete", false, "delete the configuration by locationPrefix") apply := fsConfigureCommand.Bool("apply", false, "update and apply filer configuration") @@ -84,6 +85,7 @@ func (c *commandFsConfigure) Do(args []string, commandEnv *CommandEnv, writer io Fsync: *fsync, DiskType: *diskType, VolumeGrowthCount: uint32(*volumeGrowthCount), + ReadOnly: *isReadOnly, } // check collection diff --git a/weed/shell/shell_liner.go b/weed/shell/shell_liner.go index 1dd611ca5..38b74bc54 100644 --- a/weed/shell/shell_liner.go +++ b/weed/shell/shell_liner.go @@ -31,6 +31,7 @@ func RunShell(options ShellOptions) { }) line.SetCtrlCAborts(true) + line.SetTabCompletionStyle(liner.TabPrints) setCompletionHandler() loadHistory() diff --git a/weed/storage/volume.go b/weed/storage/volume.go index e0638d8a8..c6bf3e329 100644 --- a/weed/storage/volume.go +++ b/weed/storage/volume.go @@ -180,10 +180,16 @@ func (v *Volume) Close() { v.dataFileAccessLock.Lock() defer v.dataFileAccessLock.Unlock() if v.nm != nil { + if err := v.nm.Sync(); err != nil { + glog.Warningf("Volume Close fail to sync volume idx %d", v.Id) + } v.nm.Close() v.nm = nil } if v.DataBackend != nil { + if err := v.DataBackend.Sync(); err != nil { + glog.Warningf("Volume Close fail to sync volume %d", v.Id) + } _ = v.DataBackend.Close() v.DataBackend = nil stats.VolumeServerVolumeCounter.WithLabelValues(v.Collection, "volume").Dec() diff --git a/weed/util/chunk_cache/on_disk_cache_layer.go b/weed/util/chunk_cache/on_disk_cache_layer.go index a4b3b6994..b004913ef 100644 --- a/weed/util/chunk_cache/on_disk_cache_layer.go +++ b/weed/util/chunk_cache/on_disk_cache_layer.go @@ -42,6 +42,10 @@ func NewOnDiskCacheLayer(dir, namePrefix string, diskSize int64, segmentCount in func (c *OnDiskCacheLayer) setChunk(needleId types.NeedleId, data []byte) { + if len(c.diskCaches) == 0 { + return + } + if c.diskCaches[0].fileSize+int64(len(data)) > c.diskCaches[0].sizeLimit { t, resetErr := c.diskCaches[len(c.diskCaches)-1].Reset() if resetErr != nil { diff --git a/weed/util/constants.go b/weed/util/constants.go index c6b5c1dc5..e909319eb 100644 --- a/weed/util/constants.go +++ b/weed/util/constants.go @@ -5,7 +5,7 @@ import ( ) var ( - VERSION = fmt.Sprintf("%s %d.%02d", sizeLimit, 2, 49) + VERSION = fmt.Sprintf("%s %d.%02d", sizeLimit, 2, 50) COMMIT = "" )