You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

161 lines
5.3 KiB

package weed_server
import (
"context"
"io"
"strings"
"syscall"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func (fs *FilerServer) StreamMutateEntry(stream grpc.BidiStreamingServer[filer_pb.StreamMutateEntryRequest, filer_pb.StreamMutateEntryResponse]) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
switch r := req.Request.(type) {
case *filer_pb.StreamMutateEntryRequest_CreateRequest:
resp, createErr := fs.CreateEntry(stream.Context(), r.CreateRequest)
if createErr != nil {
resp = &filer_pb.CreateEntryResponse{Error: createErr.Error()}
}
streamResp := &filer_pb.StreamMutateEntryResponse{
RequestId: req.RequestId,
IsLast: true,
Response: &filer_pb.StreamMutateEntryResponse_CreateResponse{CreateResponse: resp},
}
if resp.Error != "" {
streamResp.Error = resp.Error
streamResp.Errno = int32(syscall.EIO)
}
if sendErr := stream.Send(streamResp); sendErr != nil {
return sendErr
}
case *filer_pb.StreamMutateEntryRequest_UpdateRequest:
resp, updateErr := fs.UpdateEntry(stream.Context(), r.UpdateRequest)
if updateErr != nil {
resp = &filer_pb.UpdateEntryResponse{}
}
streamResp := &filer_pb.StreamMutateEntryResponse{
RequestId: req.RequestId,
IsLast: true,
Response: &filer_pb.StreamMutateEntryResponse_UpdateResponse{UpdateResponse: resp},
}
if updateErr != nil {
streamResp.Error = updateErr.Error()
streamResp.Errno = int32(syscall.EIO)
}
if sendErr := stream.Send(streamResp); sendErr != nil {
return sendErr
}
case *filer_pb.StreamMutateEntryRequest_DeleteRequest:
resp, deleteErr := fs.DeleteEntry(stream.Context(), r.DeleteRequest)
if deleteErr != nil {
resp = &filer_pb.DeleteEntryResponse{Error: deleteErr.Error()}
}
streamResp := &filer_pb.StreamMutateEntryResponse{
RequestId: req.RequestId,
IsLast: true,
Response: &filer_pb.StreamMutateEntryResponse_DeleteResponse{DeleteResponse: resp},
}
if resp.Error != "" {
streamResp.Error = resp.Error
streamResp.Errno = int32(syscall.EIO)
}
if sendErr := stream.Send(streamResp); sendErr != nil {
return sendErr
}
case *filer_pb.StreamMutateEntryRequest_RenameRequest:
if err := fs.handleStreamMutateRename(stream, req.RequestId, r.RenameRequest); err != nil {
return err
}
default:
glog.Warningf("StreamMutateEntry: unknown request type %T", req.Request)
}
}
}
// handleStreamMutateRename delegates to the existing StreamRenameEntry logic
// using a proxy stream that converts StreamRenameEntryResponse events into
// StreamMutateEntryResponse messages on the parent bidi stream.
func (fs *FilerServer) handleStreamMutateRename(
parent grpc.BidiStreamingServer[filer_pb.StreamMutateEntryRequest, filer_pb.StreamMutateEntryResponse],
requestId uint64,
req *filer_pb.StreamRenameEntryRequest,
) error {
proxy := &renameStreamProxy{parent: parent, requestId: requestId}
renameErr := fs.StreamRenameEntry(req, proxy)
// Always send a final is_last=true to signal rename completion.
finalResp := &filer_pb.StreamMutateEntryResponse{
RequestId: requestId,
IsLast: true,
Response: &filer_pb.StreamMutateEntryResponse_RenameResponse{
RenameResponse: &filer_pb.StreamRenameEntryResponse{},
},
}
if renameErr != nil {
finalResp.Error = renameErr.Error()
finalResp.Errno = renameErrno(renameErr)
glog.V(0).Infof("StreamMutateEntry rename: %v", renameErr)
}
if sendErr := parent.Send(finalResp); sendErr != nil {
return sendErr
}
return nil
}
// renameStreamProxy adapts the bidi StreamMutateEntry stream to look like a
// SeaweedFiler_StreamRenameEntryServer, which is what StreamRenameEntry and
// moveEntry expect. Each Send() call forwards the response as a non-final
// StreamMutateEntryResponse.
type renameStreamProxy struct {
parent grpc.BidiStreamingServer[filer_pb.StreamMutateEntryRequest, filer_pb.StreamMutateEntryResponse]
requestId uint64
}
func (p *renameStreamProxy) Send(resp *filer_pb.StreamRenameEntryResponse) error {
return p.parent.Send(&filer_pb.StreamMutateEntryResponse{
RequestId: p.requestId,
IsLast: false,
Response: &filer_pb.StreamMutateEntryResponse_RenameResponse{RenameResponse: resp},
})
}
func (p *renameStreamProxy) Context() context.Context {
return p.parent.Context()
}
func (p *renameStreamProxy) SendMsg(m any) error { return p.parent.SendMsg(m) }
func (p *renameStreamProxy) RecvMsg(m any) error { return p.parent.RecvMsg(m) }
func (p *renameStreamProxy) SetHeader(md metadata.MD) error { return p.parent.SetHeader(md) }
func (p *renameStreamProxy) SendHeader(md metadata.MD) error { return p.parent.SendHeader(md) }
func (p *renameStreamProxy) SetTrailer(md metadata.MD) { p.parent.SetTrailer(md) }
// renameErrno maps a rename error to a POSIX errno for the client.
func renameErrno(err error) int32 {
msg := err.Error()
switch {
case strings.Contains(msg, "not found"):
return int32(syscall.ENOENT)
case strings.Contains(msg, "not empty"):
return int32(syscall.ENOTEMPTY)
case strings.Contains(msg, "not directory"):
return int32(syscall.ENOTDIR)
default:
return int32(syscall.EIO)
}
}