1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2026-01-02 23:35:40 -05:00

Add Persistent Storage Support to V2Ray (#3300)

* update protogen to strip unused part

* add persistent storage support

* fix coding style

* update linter setting

* update github integration
This commit is contained in:
Xiaokang Wang (Shelikhoo)
2025-02-05 20:36:40 +00:00
committed by GitHub
parent 3ee9045697
commit 78cd513b82
25 changed files with 771 additions and 58 deletions

View File

@@ -1,8 +1,5 @@
run: run:
timeout: 5m timeout: 5m
skip-files:
- generated.*
- .pb.go
issues: issues:
new: true new: true
@@ -13,6 +10,9 @@ issues:
- linters: - linters:
- stylecheck - stylecheck
text: "ST1016:" text: "ST1016:"
exclude-files:
- generated.*
- .pb.go
linters: linters:
enable: enable:

View File

@@ -53,7 +53,7 @@ jobs:
cp ../*.deb ./ cp ../*.deb ./
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: v2ray-debian-packages name: v2ray-debian-packages
path: ./*.deb path: ./*.deb

View File

@@ -188,7 +188,7 @@ jobs:
openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST
- name: Upload ZIP file to Artifacts - name: Upload ZIP file to Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip name: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip
path: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip path: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip
@@ -217,7 +217,7 @@ jobs:
with: with:
go-version: ^1.23 go-version: ^1.23
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
path: build_artifacts path: build_artifacts
@@ -252,17 +252,17 @@ jobs:
openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >>$DGST
openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Release.unsigned name: Release.unsigned
path: build_artifacts/Release.unsigned path: build_artifacts/Release.unsigned
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Release.unsigned.dgst name: Release.unsigned.dgst
path: build_artifacts/Release.unsigned.dgst path: build_artifacts/Release.unsigned.dgst
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: v2ray-extra.zip name: v2ray-extra.zip
path: build_artifacts/v2ray-extra.zip path: build_artifacts/v2ray-extra.zip

View File

@@ -365,11 +365,12 @@ func (x *Intensity) GetProbeInterval() uint32 {
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// @Document The selectors for outbound under observation // @Document The selectors for outbound under observation
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"` SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"` ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"` ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
unknownFields protoimpl.UnknownFields PersistentProbeResult bool `protobuf:"varint,5,opt,name=persistent_probe_result,json=persistentProbeResult,proto3" json:"persistent_probe_result,omitempty"`
sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -423,6 +424,13 @@ func (x *Config) GetProbeInterval() int64 {
return 0 return 0
} }
func (x *Config) GetPersistentProbeResult() bool {
if x != nil {
return x.PersistentProbeResult
}
return false
}
var File_app_observatory_config_proto protoreflect.FileDescriptor var File_app_observatory_config_proto protoreflect.FileDescriptor
var file_app_observatory_config_proto_rawDesc = string([]byte{ var file_app_observatory_config_proto_rawDesc = string([]byte{
@@ -476,24 +484,28 @@ var file_app_observatory_config_proto_rawDesc = string([]byte{
0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a,
0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x76, 0x61, 0x6c, 0x22, 0x9d, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xd5, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72,
0x6f, 0x62, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x62, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x62, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x72, 0x6f, 0x62, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65,
0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x3a, 0x24, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36,
0x82, 0xb5, 0x18, 0x20, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x15, 0x62, 0x0a, 0x17, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f,
0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
0x74, 0x6f, 0x72, 0x79, 0x42, 0x6f, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x15, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65,
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3a, 0x24, 0x82, 0xb5, 0x18, 0x20, 0x0a, 0x07, 0x73, 0x65,
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x15, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x6f, 0x0a, 0x1e,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61,
0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01,
0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66,
0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35,
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79,
0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70,
0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}) })
var ( var (

View File

@@ -84,4 +84,6 @@ message Config {
string probe_url = 3; string probe_url = 3;
int64 probe_interval = 4; int64 probe_interval = 4;
bool persistent_probe_result = 5;
} }

View File

@@ -12,6 +12,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/v2fly/v2ray-core/v5/app/persistentstorage"
"github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
core "github.com/v2fly/v2ray-core/v5" core "github.com/v2fly/v2ray-core/v5"
@@ -34,7 +39,10 @@ type Observer struct {
finished *done.Instance finished *done.Instance
ohm outbound.Manager ohm outbound.Manager
persistStorage persistentstorage.ScopedPersistentStorage
persistOutboundStatusProtoStorage protostorage.ProtoPersistentStorage
} }
func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
@@ -47,6 +55,24 @@ func (o *Observer) Type() interface{} {
func (o *Observer) Start() error { func (o *Observer) Start() error {
if o.config != nil && len(o.config.SubjectSelector) != 0 { if o.config != nil && len(o.config.SubjectSelector) != 0 {
if o.config.PersistentProbeResult {
appEnvironment := envctx.EnvironmentFromContext(o.ctx).(environment.AppEnvironment)
o.persistStorage = appEnvironment.PersistentStorage()
outboundStatusStorage, err := o.persistStorage.NarrowScope(o.ctx, []byte("outbound_status"))
if err != nil {
return newError("failed to get persistent storage for outbound_status").Base(err)
}
o.persistOutboundStatusProtoStorage = outboundStatusStorage.(protostorage.ProtoPersistentStorage)
list, err := outboundStatusStorage.List(o.ctx, []byte(""))
if err != nil {
newError("failed to list persisted outbound status").Base(err).WriteToLog()
} else {
for _, v := range list {
o.loadOutboundStatus(string(v))
}
}
}
o.finished = done.New() o.finished = done.New()
go o.background() go o.background()
} }
@@ -195,6 +221,12 @@ func (o *Observer) updateStatusForResult(outbound string, result *ProbeResult) {
status.LastErrorReason = result.LastErrorReason status.LastErrorReason = result.LastErrorReason
status.Delay = 99999999 status.Delay = 99999999
} }
if o.config.PersistentProbeResult {
err := o.persistOutboundStatusProtoStorage.PutProto(o.ctx, outbound, status)
if err != nil {
newError("failed to persist outbound status").Base(err).WriteToLog()
}
}
} }
func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int { func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
@@ -206,19 +238,33 @@ func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
return -1 return -1
} }
func (o *Observer) loadOutboundStatus(name string) {
if o.persistOutboundStatusProtoStorage == nil {
return
}
status := &OutboundStatus{}
err := o.persistOutboundStatusProtoStorage.GetProto(o.ctx, name, status)
if err != nil {
newError("failed to load outbound status").Base(err).WriteToLog()
return
}
o.status = append(o.status, status)
}
func New(ctx context.Context, config *Config) (*Observer, error) { func New(ctx context.Context, config *Config) (*Observer, error) {
var outboundManager outbound.Manager obs := &Observer{
config: config,
ctx: ctx,
}
err := core.RequireFeatures(ctx, func(om outbound.Manager) { err := core.RequireFeatures(ctx, func(om outbound.Manager) {
outboundManager = om obs.ohm = om
}) })
if err != nil { if err != nil {
return nil, newError("Cannot get depended features").Base(err) return nil, newError("Cannot get depended features").Base(err)
} }
return &Observer{
config: config, return obs, nil
ctx: ctx,
ohm: outboundManager,
}, nil
} }
func init() { func init() {

View File

@@ -0,0 +1,214 @@
package filesystemstorage
import (
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type StateStorageRoot int32
const (
StateStorageRoot_WorkDir StateStorageRoot = 0
)
// Enum value maps for StateStorageRoot.
var (
StateStorageRoot_name = map[int32]string{
0: "WorkDir",
}
StateStorageRoot_value = map[string]int32{
"WorkDir": 0,
}
)
func (x StateStorageRoot) Enum() *StateStorageRoot {
p := new(StateStorageRoot)
*p = x
return p
}
func (x StateStorageRoot) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (StateStorageRoot) Descriptor() protoreflect.EnumDescriptor {
return file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0].Descriptor()
}
func (StateStorageRoot) Type() protoreflect.EnumType {
return &file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0]
}
func (x StateStorageRoot) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use StateStorageRoot.Descriptor instead.
func (StateStorageRoot) EnumDescriptor() ([]byte, []int) {
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0}
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
StateStorageRoot StateStorageRoot `protobuf:"varint,1,opt,name=state_storage_root,json=stateStorageRoot,proto3,enum=v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot" json:"state_storage_root,omitempty"`
InstanceName string `protobuf:"bytes,4,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"`
Protojson bool `protobuf:"varint,5,opt,name=protojson,proto3" json:"protojson,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetStateStorageRoot() StateStorageRoot {
if x != nil {
return x.StateStorageRoot
}
return StateStorageRoot_WorkDir
}
func (x *Config) GetInstanceName() string {
if x != nil {
return x.InstanceName
}
return ""
}
func (x *Config) GetProtojson() bool {
if x != nil {
return x.Protojson
}
return false
}
var File_app_persistentstorage_filesystemstorage_config_proto protoreflect.FileDescriptor
var file_app_persistentstorage_filesystemstorage_config_proto_rawDesc = string([]byte{
0x0a, 0x34, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74,
0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x32, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e,
0x74, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73,
0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x01, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x72, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65,
0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x44, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65,
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69,
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20,
0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x20,
0x82, 0xb5, 0x18, 0x1c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x11, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2a, 0x1f, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x52, 0x6f, 0x6f, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x69, 0x72, 0x10,
0x00, 0x42, 0xb3, 0x01, 0x0a, 0x32, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72,
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70,
0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0xaa, 0x02, 0x32, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e,
0x41, 0x70, 0x70, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce sync.Once
file_app_persistentstorage_filesystemstorage_config_proto_rawDescData []byte
)
func file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP() []byte {
file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce.Do(func() {
file_app_persistentstorage_filesystemstorage_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc)))
})
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescData
}
var file_app_persistentstorage_filesystemstorage_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_app_persistentstorage_filesystemstorage_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_app_persistentstorage_filesystemstorage_config_proto_goTypes = []any{
(StateStorageRoot)(0), // 0: v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot
(*Config)(nil), // 1: v2ray.core.app.persistentstorage.filesystemstorage.Config
}
var file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = []int32{
0, // 0: v2ray.core.app.persistentstorage.filesystemstorage.Config.state_storage_root:type_name -> v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_app_persistentstorage_filesystemstorage_config_proto_init() }
func file_app_persistentstorage_filesystemstorage_config_proto_init() {
if File_app_persistentstorage_filesystemstorage_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc)),
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_persistentstorage_filesystemstorage_config_proto_goTypes,
DependencyIndexes: file_app_persistentstorage_filesystemstorage_config_proto_depIdxs,
EnumInfos: file_app_persistentstorage_filesystemstorage_config_proto_enumTypes,
MessageInfos: file_app_persistentstorage_filesystemstorage_config_proto_msgTypes,
}.Build()
File_app_persistentstorage_filesystemstorage_config_proto = out.File
file_app_persistentstorage_filesystemstorage_config_proto_goTypes = nil
file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = nil
}

View File

@@ -0,0 +1,23 @@
syntax = "proto3";
package v2ray.core.app.persistentstorage.filesystemstorage;
option csharp_namespace = "V2Ray.Core.App.Persistentstorage.Filesystemstorage";
option go_package = "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage";
option java_package = "com.v2ray.core.persistentstorage.filesystemstorage";
option java_multiple_files = true;
import "common/protoext/extensions.proto";
enum StateStorageRoot {
WorkDir = 0;
}
message Config {
option (v2ray.core.common.protoext.message_opt).type = "service";
option (v2ray.core.common.protoext.message_opt).short_name = "filesystemstorage";
StateStorageRoot state_storage_root = 1;
string instance_name = 4;
bool protojson = 5;
}

View File

@@ -0,0 +1,133 @@
package filesystemstorage
import (
"bytes"
"context"
"io"
"path/filepath"
"strings"
"google.golang.org/protobuf/proto"
"github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
)
func newFileSystemStorage(ctx context.Context, config *Config) storage.ScopedPersistentStorageService {
appEnvironment := envctx.EnvironmentFromContext(ctx).(environment.AppEnvironment)
fss := &fileSystemStorage{
fs: appEnvironment,
pathRoot: config.InstanceName,
currentLocation: nil,
config: config,
}
protoStorageInst := protostorage.NewProtoStorage(fss, config.Protojson)
fss.proto = protoStorageInst
return fss
}
type fileSystemStorage struct {
fs environment.FileSystemCapabilitySet
proto protostorage.ProtoPersistentStorage
pathRoot string
currentLocation []string
config *Config
}
func (f *fileSystemStorage) Type() interface{} {
return storage.ScopedPersistentStorageServiceType
}
func (f *fileSystemStorage) Start() error {
return nil
}
func (f *fileSystemStorage) Close() error {
return nil
}
func (f *fileSystemStorage) PutProto(ctx context.Context, key string, pb proto.Message) error {
return f.proto.PutProto(ctx, key, pb)
}
func (f *fileSystemStorage) GetProto(ctx context.Context, key string, pb proto.Message) error {
return f.proto.GetProto(ctx, key, pb)
}
func (f *fileSystemStorage) ScopedPersistentStorageEngine() {
}
func (f *fileSystemStorage) Put(ctx context.Context, key []byte, value []byte) error {
finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key))
if value == nil {
return f.fs.RemoveFile()(finalPath)
}
writer, err := f.fs.OpenFileForWrite()(finalPath)
if err != nil {
return err
}
defer writer.Close()
_, err = io.Copy(writer, io.NopCloser(bytes.NewReader(value)))
return err
}
func (f *fileSystemStorage) Get(ctx context.Context, key []byte) ([]byte, error) {
finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key))
reader, err := f.fs.OpenFileForRead()(finalPath)
if err != nil {
return nil, err
}
defer reader.Close()
return io.ReadAll(reader)
}
func (f *fileSystemStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) {
res, err := f.fs.ReadDir()(filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...)))
if err != nil {
return nil, err
}
var result [][]byte
for _, entry := range res {
if !entry.IsDir() && bytes.HasPrefix([]byte(entry.Name()), keyPrefix) {
result = append(result, []byte(entry.Name()))
}
}
return result, nil
}
func (f *fileSystemStorage) Clear(ctx context.Context) {
allFile, err := f.List(ctx, []byte{})
if err != nil {
return
}
for _, file := range allFile {
_ = f.Put(ctx, file, nil)
}
}
func (f *fileSystemStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) {
escapedKey := strings.ReplaceAll(string(key), "/", "_")
fss := &fileSystemStorage{
fs: f.fs,
pathRoot: f.pathRoot,
currentLocation: append(f.currentLocation, escapedKey),
config: f.config,
}
fss.proto = protostorage.NewProtoStorage(fss, f.config.Protojson)
return fss, nil
}
func (f *fileSystemStorage) DropScope(ctx context.Context, key []byte) error {
panic("unimplemented")
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return newFileSystemStorage(ctx, config.(*Config)), nil
}))
}

View File

@@ -0,0 +1,51 @@
package protostorage
import (
"context"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
)
type ProtoPersistentStorage interface {
PutProto(ctx context.Context, key string, pb proto.Message) error
GetProto(ctx context.Context, key string, pb proto.Message) error
}
type protoStorage struct {
storage storage.ScopedPersistentStorage
textFormat bool
}
func (p *protoStorage) PutProto(ctx context.Context, key string, pb proto.Message) error {
if !p.textFormat {
data, err := proto.Marshal(pb)
if err != nil {
return err
}
return p.storage.Put(ctx, []byte(key), data)
} else {
protojsonStr := protojson.Format(pb)
return p.storage.Put(ctx, []byte(key), []byte(protojsonStr))
}
}
func (p *protoStorage) GetProto(ctx context.Context, key string, pb proto.Message) error {
data, err := p.storage.Get(ctx, []byte(key))
if err != nil {
return err
}
if !p.textFormat {
return proto.Unmarshal(data, pb)
}
return protojson.Unmarshal(data, pb)
}
func NewProtoStorage(storage storage.ScopedPersistentStorage, textFormat bool) ProtoPersistentStorage {
return &protoStorage{
storage: storage,
textFormat: textFormat,
}
}

View File

@@ -0,0 +1,5 @@
package persistentstorage
import "github.com/v2fly/v2ray-core/v5/features/extension/storage"
type ScopedPersistentStorage = storage.ScopedPersistentStorage

View File

@@ -5,18 +5,18 @@ import (
"testing" "testing"
_ "unsafe" _ "unsafe"
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
"google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/anypb"
core "github.com/v2fly/v2ray-core/v5" core "github.com/v2fly/v2ray-core/v5"
"github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/policy"
. "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" . "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound"
"github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/app/stats"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
"github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/serial"
"github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/outbound"
@@ -50,7 +50,11 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
v.AddFeature((outbound.Manager)(new(Manager))) v.AddFeature((outbound.Manager)(new(Manager)))
ctx := toContext(context.Background(), v) ctx := toContext(context.Background(), v)
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener()) defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
rootEnv := environment.NewRootEnvImpl(ctx,
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
defaultFilesystemImpl, deferredPersistentStorageImpl)
proxyEnvironment := rootEnv.ProxyEnvironment("o") proxyEnvironment := rootEnv.ProxyEnvironment("o")
ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment) ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment)
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
@@ -83,7 +87,11 @@ func TestOutboundWithStatCounter(t *testing.T) {
v.AddFeature((outbound.Manager)(new(Manager))) v.AddFeature((outbound.Manager)(new(Manager)))
ctx := toContext(context.Background(), v) ctx := toContext(context.Background(), v)
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener()) defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
rootEnv := environment.NewRootEnvImpl(ctx,
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
defaultFilesystemImpl, deferredPersistentStorageImpl)
proxyEnvironment := rootEnv.ProxyEnvironment("o") proxyEnvironment := rootEnv.ProxyEnvironment("o")
ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment) ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment)
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{

View File

@@ -0,0 +1,111 @@
package deferredpersistentstorage
import (
"context"
"github.com/v2fly/v2ray-core/v5/app/persistentstorage"
"github.com/v2fly/v2ray-core/v5/common/errors"
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
)
type DeferredPersistentStorage interface {
storage.ScopedPersistentStorage
ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage)
}
var errNotExist = errors.New("persistent storage does not exist")
type deferredPersistentStorage struct {
ready context.Context
done context.CancelFunc
inner persistentstorage.ScopedPersistentStorage
awaitingChildren []*deferredPersistentStorage
intoScopes []string
}
func (d *deferredPersistentStorage) ScopedPersistentStorageEngine() {
}
func (d *deferredPersistentStorage) Put(ctx context.Context, key []byte, value []byte) error {
<-d.ready.Done()
if d.inner == nil {
return errNotExist
}
return d.inner.Put(ctx, key, value)
}
func (d *deferredPersistentStorage) Get(ctx context.Context, key []byte) ([]byte, error) {
<-d.ready.Done()
if d.inner == nil {
return nil, errNotExist
}
return d.inner.Get(ctx, key)
}
func (d *deferredPersistentStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) {
<-d.ready.Done()
if d.inner == nil {
return nil, errNotExist
}
return d.inner.List(ctx, keyPrefix)
}
func (d *deferredPersistentStorage) Clear(ctx context.Context) {
<-d.ready.Done()
if d.inner == nil {
return
}
d.inner.Clear(ctx)
}
func (d *deferredPersistentStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) {
if d.ready.Err() != nil {
return d.inner.NarrowScope(ctx, key)
}
ready, done := context.WithCancel(ctx)
swallowCopyScopes := d.intoScopes
dps := &deferredPersistentStorage{
ready: ready,
done: done,
inner: nil,
intoScopes: append(swallowCopyScopes, string(key)),
}
d.awaitingChildren = append(d.awaitingChildren, dps)
return dps, nil
}
func (d *deferredPersistentStorage) DropScope(ctx context.Context, key []byte) error {
<-d.ready.Done()
if d.inner == nil {
return errNotExist
}
return d.inner.DropScope(ctx, key)
}
func (d *deferredPersistentStorage) ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage) {
d.inner = inner
if inner != nil {
for _, scope := range d.intoScopes {
newScope, err := inner.NarrowScope(ctx, []byte(scope))
if err != nil {
panic(err)
}
d.inner = newScope
}
}
for _, child := range d.awaitingChildren {
child.ProvideInner(ctx, d.inner)
}
d.done()
}
func NewDeferredPersistentStorage(ctx context.Context) DeferredPersistentStorage {
ready, done := context.WithCancel(ctx)
return &deferredPersistentStorage{
ready: ready,
done: done,
inner: nil,
}
}

View File

@@ -8,6 +8,14 @@ import (
type fileSystemDefaultImpl struct{} type fileSystemDefaultImpl struct{}
func (f fileSystemDefaultImpl) ReadDir() fsifce.FileReadDirFunc {
return filesystem.NewFileReadDir
}
func (f fileSystemDefaultImpl) RemoveFile() fsifce.FileRemoveFunc {
return filesystem.NewFileRemover
}
func (f fileSystemDefaultImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc { func (f fileSystemDefaultImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
return filesystem.NewFileSeeker return filesystem.NewFileSeeker
} }

View File

@@ -6,4 +6,6 @@ type FileSystemCapabilitySet interface {
OpenFileForReadSeek() fsifce.FileSeekerFunc OpenFileForReadSeek() fsifce.FileSeekerFunc
OpenFileForRead() fsifce.FileReaderFunc OpenFileForRead() fsifce.FileReaderFunc
OpenFileForWrite() fsifce.FileWriterFunc OpenFileForWrite() fsifce.FileWriterFunc
ReadDir() fsifce.FileReadDirFunc
RemoveFile() fsifce.FileRemoveFunc
} }

View File

@@ -0,0 +1,33 @@
package filesystemimpl
import (
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/platform/filesystem"
"github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce"
)
func NewDefaultFileSystemDefaultImpl() environment.FileSystemCapabilitySet {
return fsCapImpl{}
}
type fsCapImpl struct{}
func (f fsCapImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
return filesystem.NewFileSeeker
}
func (f fsCapImpl) OpenFileForRead() fsifce.FileReaderFunc {
return filesystem.NewFileReader
}
func (f fsCapImpl) OpenFileForWrite() fsifce.FileWriterFunc {
return filesystem.NewFileWriter
}
func (f fsCapImpl) ReadDir() fsifce.FileReadDirFunc {
return filesystem.NewFileReadDir
}
func (f fsCapImpl) RemoveFile() fsifce.FileRemoveFunc {
return filesystem.NewFileRemover
}

View File

@@ -11,19 +11,24 @@ import (
func NewRootEnvImpl(ctx context.Context, transientStorage storage.ScopedTransientStorage, func NewRootEnvImpl(ctx context.Context, transientStorage storage.ScopedTransientStorage,
systemDialer internet.SystemDialer, systemListener internet.SystemListener, systemDialer internet.SystemDialer, systemListener internet.SystemListener,
filesystem FileSystemCapabilitySet, persistStorage storage.ScopedPersistentStorage,
) RootEnvironment { ) RootEnvironment {
return &rootEnvImpl{ return &rootEnvImpl{
transientStorage: transientStorage, transientStorage: transientStorage,
systemListener: systemListener, systemListener: systemListener,
systemDialer: systemDialer, systemDialer: systemDialer,
filesystem: filesystem,
persistStorage: persistStorage,
ctx: ctx, ctx: ctx,
} }
} }
type rootEnvImpl struct { type rootEnvImpl struct {
persistStorage storage.ScopedPersistentStorage
transientStorage storage.ScopedTransientStorage transientStorage storage.ScopedTransientStorage
systemDialer internet.SystemDialer systemDialer internet.SystemDialer
systemListener internet.SystemListener systemListener internet.SystemListener
filesystem FileSystemCapabilitySet
ctx context.Context ctx context.Context
} }
@@ -37,10 +42,16 @@ func (r *rootEnvImpl) AppEnvironment(tag string) AppEnvironment {
if err != nil { if err != nil {
return nil return nil
} }
persistStorage, err := r.persistStorage.NarrowScope(r.ctx, []byte(tag))
if err != nil {
return nil
}
return &appEnvImpl{ return &appEnvImpl{
transientStorage: transientStorage, transientStorage: transientStorage,
persistStorage: persistStorage,
systemListener: r.systemListener, systemListener: r.systemListener,
systemDialer: r.systemDialer, systemDialer: r.systemDialer,
filesystem: r.filesystem,
ctx: r.ctx, ctx: r.ctx,
} }
} }
@@ -67,9 +78,11 @@ func (r *rootEnvImpl) DropProxyEnvironment(tag string) error {
} }
type appEnvImpl struct { type appEnvImpl struct {
persistStorage storage.ScopedPersistentStorage
transientStorage storage.ScopedTransientStorage transientStorage storage.ScopedTransientStorage
systemDialer internet.SystemDialer systemDialer internet.SystemDialer
systemListener internet.SystemListener systemListener internet.SystemListener
filesystem FileSystemCapabilitySet
ctx context.Context ctx context.Context
} }
@@ -95,19 +108,27 @@ func (a *appEnvImpl) OutboundDialer() tagged.DialFunc {
} }
func (a *appEnvImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc { func (a *appEnvImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
panic("implement me") return a.filesystem.OpenFileForReadSeek()
} }
func (a *appEnvImpl) OpenFileForRead() fsifce.FileReaderFunc { func (a *appEnvImpl) OpenFileForRead() fsifce.FileReaderFunc {
panic("implement me") return a.filesystem.OpenFileForRead()
} }
func (a *appEnvImpl) OpenFileForWrite() fsifce.FileWriterFunc { func (a *appEnvImpl) OpenFileForWrite() fsifce.FileWriterFunc {
panic("implement me") return a.filesystem.OpenFileForWrite()
}
func (a *appEnvImpl) ReadDir() fsifce.FileReadDirFunc {
return a.filesystem.ReadDir()
}
func (a *appEnvImpl) RemoveFile() fsifce.FileRemoveFunc {
return a.filesystem.RemoveFile()
} }
func (a *appEnvImpl) PersistentStorage() storage.ScopedPersistentStorage { func (a *appEnvImpl) PersistentStorage() storage.ScopedPersistentStorage {
panic("implement me") return a.persistStorage
} }
func (a *appEnvImpl) TransientStorage() storage.ScopedTransientStorage { func (a *appEnvImpl) TransientStorage() storage.ScopedTransientStorage {

View File

@@ -3,6 +3,7 @@ package filesystem
import ( import (
"io" "io"
"os" "os"
"path/filepath"
"github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/buf"
"github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/platform"
@@ -18,9 +19,17 @@ var NewFileReader fsifce.FileReaderFunc = func(path string) (io.ReadCloser, erro
} }
var NewFileWriter fsifce.FileWriterFunc = func(path string) (io.WriteCloser, error) { var NewFileWriter fsifce.FileWriterFunc = func(path string) (io.WriteCloser, error) {
basePath := filepath.Dir(path)
if err := os.MkdirAll(basePath, 0o700); err != nil {
return nil, err
}
return os.Create(path) return os.Create(path)
} }
var NewFileRemover fsifce.FileRemoveFunc = os.Remove
var NewFileReadDir fsifce.FileReadDirFunc = os.ReadDir
func ReadFile(path string) ([]byte, error) { func ReadFile(path string) ([]byte, error) {
reader, err := NewFileReader(path) reader, err := NewFileReader(path)
if err != nil { if err != nil {

View File

@@ -1,9 +1,16 @@
package fsifce package fsifce
import "io" import (
"io"
"io/fs"
)
type FileSeekerFunc func(path string) (io.ReadSeekCloser, error) type FileSeekerFunc func(path string) (io.ReadSeekCloser, error)
type FileReaderFunc func(path string) (io.ReadCloser, error) type FileReaderFunc func(path string) (io.ReadCloser, error)
type FileWriterFunc func(path string) (io.WriteCloser, error) type FileWriterFunc func(path string) (io.WriteCloser, error)
type FileReadDirFunc func(path string) ([]fs.DirEntry, error)
type FileRemoveFunc func(path string) error

View File

@@ -2,6 +2,8 @@ package storage
import ( import (
"context" "context"
"github.com/v2fly/v2ray-core/v5/features"
) )
type ScopedPersistentStorage interface { type ScopedPersistentStorage interface {
@@ -23,3 +25,10 @@ type ScopedTransientStorage interface {
NarrowScope(ctx context.Context, key string) (ScopedTransientStorage, error) NarrowScope(ctx context.Context, key string) (ScopedTransientStorage, error)
DropScope(ctx context.Context, key string) error DropScope(ctx context.Context, key string) error
} }
type ScopedPersistentStorageService interface {
ScopedPersistentStorage
features.Feature
}
var ScopedPersistentStorageServiceType = (*ScopedPersistentStorageService)(nil)

View File

@@ -108,9 +108,9 @@ func getProjectProtocVersion(url string) (string, error) {
if err != nil { if err != nil {
return "", fmt.Errorf("can not read from body") return "", fmt.Errorf("can not read from body")
} }
versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v(\d+\.\d+\.\d+)`) versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v(\d+\.(\d+\.\d+))`)
matched := versionRegexp.FindStringSubmatch(string(body)) matched := versionRegexp.FindStringSubmatch(string(body))
return matched[1], nil return matched[2], nil
} }
func getInstalledProtocVersion(protocPath string) (string, error) { func getInstalledProtocVersion(protocPath string) (string, error) {
@@ -126,10 +126,6 @@ func getInstalledProtocVersion(protocPath string) (string, error) {
if len(matched) == 0 { if len(matched) == 0 {
return "", errors.New("can not parse protoc version") return "", errors.New("can not parse protoc version")
} }
if len(matched) == 2 {
installedVersion += "4." // in contrast to getProjectProtocVersion()
}
installedVersion += matched[1] installedVersion += matched[1]
fmt.Println("Using protoc version: " + installedVersion) fmt.Println("Using protoc version: " + installedVersion)
return installedVersion, nil return installedVersion, nil
@@ -139,9 +135,6 @@ func parseVersion(s string, width int) int64 {
strList := strings.Split(s, ".") strList := strings.Split(s, ".")
format := fmt.Sprintf("%%s%%0%ds", width) format := fmt.Sprintf("%%s%%0%ds", width)
v := "" v := ""
if len(strList) == 2 {
strList = append([]string{"4"}, strList...)
}
for _, value := range strList { for _, value := range strList {
v = fmt.Sprintf(format, v, value) v = fmt.Sprintf(format, v, value)
} }

View File

@@ -34,6 +34,7 @@ import (
_ "github.com/v2fly/v2ray-core/v5/app/commander/webcommander" _ "github.com/v2fly/v2ray-core/v5/app/commander/webcommander"
_ "github.com/v2fly/v2ray-core/v5/app/instman" _ "github.com/v2fly/v2ray-core/v5/app/instman"
_ "github.com/v2fly/v2ray-core/v5/app/observatory" _ "github.com/v2fly/v2ray-core/v5/app/observatory"
_ "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage"
_ "github.com/v2fly/v2ray-core/v5/app/tun" _ "github.com/v2fly/v2ray-core/v5/app/tun"
// Inbound and outbound proxies. // Inbound and outbound proxies.

View File

@@ -7,6 +7,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
"github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
@@ -25,7 +28,11 @@ import (
func TestDialAndListen(t *testing.T) { func TestDialAndListen(t *testing.T) {
ctx := context.Background() ctx := context.Background()
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener()) defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
rootEnv := environment.NewRootEnvImpl(ctx,
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
defaultFilesystemImpl, deferredPersistentStorageImpl)
proxyEnvironment := rootEnv.ProxyEnvironment("o") proxyEnvironment := rootEnv.ProxyEnvironment("o")
transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp") transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp")
if err != nil { if err != nil {

View File

@@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet"
) )

View File

@@ -5,6 +5,10 @@ import (
"reflect" "reflect"
sync "sync" sync "sync"
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
"github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
@@ -205,7 +209,14 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
} }
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
server.env = environment.NewRootEnvImpl(server.ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener()) defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(server.ctx)
server.env = environment.NewRootEnvImpl(server.ctx,
transientstorageimpl.NewScopedTransientStorageImpl(),
defaultNetworkImpl.Dialer(),
defaultNetworkImpl.Listener(),
defaultFilesystemImpl,
deferredPersistentStorageImpl)
for _, appSettings := range config.App { for _, appSettings := range config.App {
settings, err := serial.GetInstanceOf(appSettings) settings, err := serial.GetInstanceOf(appSettings)
@@ -247,6 +258,12 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
return true, newError("not all dependency are resolved.") return true, newError("not all dependency are resolved.")
} }
if persistentStorageService := server.GetFeature(storage.ScopedPersistentStorageServiceType); persistentStorageService != nil {
deferredPersistentStorageImpl.ProvideInner(server.ctx, persistentStorageService.(storage.ScopedPersistentStorage))
} else {
deferredPersistentStorageImpl.ProvideInner(server.ctx, nil)
}
if err := addInboundHandlers(server, config.Inbound); err != nil { if err := addInboundHandlers(server, config.Inbound); err != nil {
return true, err return true, err
} }