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