go-hep.org/x/hep@v0.38.1/xrootd/xrdproto/dirlist/dirlist.go (about)

     1  // Copyright ©2018 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package dirlist contains the structures describing request and response
     6  // for dirlist request used to obtain the contents of a directory.
     7  package dirlist // import "go-hep.org/x/hep/xrootd/xrdproto/dirlist"
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"go-hep.org/x/hep/xrootd/internal/xrdenc"
    15  	"go-hep.org/x/hep/xrootd/xrdfs"
    16  	"go-hep.org/x/hep/xrootd/xrdproto"
    17  )
    18  
    19  // RequestID is the id of the request, it is sent as part of message.
    20  // See xrootd protocol specification for details: http://xrootd.org/doc/dev45/XRdv310.pdf, 2.3 Client Request Format.
    21  const RequestID uint16 = 3004
    22  
    23  // Response is a response for the dirlist request,
    24  // which contains a slice of entries containing the entry name and the entry stat info,
    25  // and a WithStatInfo flag indicating whether a request asked for a stat info.
    26  type Response struct {
    27  	Entries      []xrdfs.EntryStat
    28  	WithStatInfo bool
    29  }
    30  
    31  // RespID implements xrdproto.Response.RespID.
    32  func (resp *Response) RespID() uint16 { return RequestID }
    33  
    34  // MarshalXrd implements xrdproto.Marshaler.
    35  func (o Response) MarshalXrd(wBuffer *xrdenc.WBuffer) error {
    36  	entries := o.Entries
    37  	if o.WithStatInfo {
    38  		firstEntry := []xrdfs.EntryStat{
    39  			{EntryName: ".", HasStatInfo: true},
    40  		}
    41  		entries = append(firstEntry, entries...)
    42  	}
    43  
    44  	consistent := true
    45  	for i := range entries {
    46  		if entries[i].HasStatInfo != o.WithStatInfo {
    47  			consistent = false
    48  		}
    49  	}
    50  
    51  	if !consistent {
    52  		// TODO: keep this error or remove it?
    53  		return errors.New("xrootd: all entries of dirlist.Response should either have stat info or not")
    54  	}
    55  
    56  	for i := range entries {
    57  		nameSeparator := "\n"
    58  		statInfoSeparator := "\n"
    59  		if i == len(entries)-1 {
    60  			if o.WithStatInfo {
    61  				statInfoSeparator = "\x00"
    62  			} else {
    63  				nameSeparator = "\x00"
    64  			}
    65  		}
    66  
    67  		wBuffer.WriteBytes([]byte(entries[i].EntryName + nameSeparator))
    68  		if !o.WithStatInfo {
    69  			continue
    70  		}
    71  
    72  		if err := entries[i].MarshalXrd(wBuffer); err != nil {
    73  			return err
    74  		}
    75  
    76  		wBuffer.WriteBytes([]byte(statInfoSeparator))
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  // UnmarshalXrd implements xrdproto.Unmarshaler
    83  // When stat information is supported by the server, the format is
    84  //
    85  //	".\n"
    86  //	"0 0 0 0\n"
    87  //	"dirname\n"
    88  //	"id size flags modtime\n"
    89  //	...
    90  //	0
    91  //
    92  // Otherwise, the format is the following:
    93  //
    94  //	"dirname\n"
    95  //	...
    96  //	0
    97  //
    98  // See xrootd protocol specification, page 45 for further details.
    99  func (o *Response) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error {
   100  	if rBuffer.Len() == 0 {
   101  		return nil
   102  	}
   103  
   104  	data := bytes.TrimRight(rBuffer.Bytes(), "\x00")
   105  	lines := bytes.Split(data, []byte{'\n'})
   106  
   107  	// FIXME(sbinet): drop the extra call to bytes.Equal when
   108  	//  https://github.com/xrootd/xrootd/issues/739
   109  	// is fixed or clarified.
   110  	if !(bytes.HasPrefix(data, []byte(".\n0 0 0 0\n")) || bytes.Equal(data, []byte(".\n0 0 0 0"))) {
   111  		// That means that the server doesn't support returning stat information.
   112  		o.Entries = make([]xrdfs.EntryStat, len(lines))
   113  		o.WithStatInfo = false
   114  		for i, v := range lines {
   115  			o.Entries[i] = xrdfs.EntryStat{EntryName: string(v)}
   116  		}
   117  		return nil
   118  	}
   119  
   120  	if len(lines)%2 != 0 {
   121  		return fmt.Errorf("xrootd: wrong response size for the dirlist request: want even number of lines, got %d", len(lines))
   122  	}
   123  
   124  	lines = lines[2:]
   125  	o.Entries = make([]xrdfs.EntryStat, len(lines)/2)
   126  	o.WithStatInfo = true
   127  
   128  	for i := 0; i < len(lines); i += 2 {
   129  		var rbuf = xrdenc.NewRBuffer(lines[i+1])
   130  		err := o.Entries[i/2].UnmarshalXrd(rbuf)
   131  		if err != nil {
   132  			return err
   133  		}
   134  		o.Entries[i/2].EntryName = string(lines[i])
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // Request holds the dirlist request parameters.
   141  type Request struct {
   142  	_       [15]byte
   143  	Options RequestOptions
   144  	Path    string
   145  }
   146  
   147  // RequestOptions specifies what should be returned as part of response.
   148  type RequestOptions byte
   149  
   150  const (
   151  	None         RequestOptions = 0 // None specifies that no addition information except entry names is required.
   152  	WithStatInfo RequestOptions = 2 // WithStatInfo specifies that stat information for every entry is required.
   153  )
   154  
   155  // NewRequest forms a Request according to provided path.
   156  func NewRequest(path string) *Request {
   157  	return &Request{Options: WithStatInfo, Path: path}
   158  }
   159  
   160  // ReqID implements xrdproto.Request.ReqID.
   161  func (req *Request) ReqID() uint16 { return RequestID }
   162  
   163  // ShouldSign implements xrdproto.Request.ShouldSign.
   164  func (req *Request) ShouldSign() bool { return false }
   165  
   166  // MarshalXrd implements xrdproto.Marshaler.
   167  func (o Request) MarshalXrd(wBuffer *xrdenc.WBuffer) error {
   168  	wBuffer.Next(15)
   169  	wBuffer.WriteU8(byte(o.Options))
   170  	wBuffer.WriteStr(o.Path)
   171  	return nil
   172  }
   173  
   174  // UnmarshalXrd implements xrdproto.Unmarshaler.
   175  func (o *Request) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error {
   176  	rBuffer.Skip(15)
   177  	o.Options = RequestOptions(rBuffer.ReadU8())
   178  	o.Path = rBuffer.ReadStr()
   179  	return nil
   180  }
   181  
   182  // Opaque implements xrdproto.FilepathRequest.Opaque.
   183  func (req *Request) Opaque() string {
   184  	return xrdproto.Opaque(req.Path)
   185  }
   186  
   187  // SetOpaque implements xrdproto.FilepathRequest.SetOpaque.
   188  func (req *Request) SetOpaque(opaque string) {
   189  	xrdproto.SetOpaque(&req.Path, opaque)
   190  }