1// Copyright (C) 2021 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package ulfs 5 6import ( 7 "context" 8 "io" 9 "os" 10 "time" 11 12 "github.com/zeebo/clingy" 13 "github.com/zeebo/errs" 14 15 "storj.io/storj/cmd/uplinkng/ulloc" 16 "storj.io/uplink" 17) 18 19// ListOptions describes options to the List command. 20type ListOptions struct { 21 Recursive bool 22 Pending bool 23 Expanded bool 24} 25 26func (lo *ListOptions) isRecursive() bool { return lo != nil && lo.Recursive } 27func (lo *ListOptions) isPending() bool { return lo != nil && lo.Pending } 28 29// RemoveOptions describes options to the Remove command. 30type RemoveOptions struct { 31 Pending bool 32} 33 34func (ro *RemoveOptions) isPending() bool { return ro != nil && ro.Pending } 35 36// OpenOptions describes options for Filesystem.Open. 37type OpenOptions struct { 38 Offset int64 39 Length int64 40} 41 42// Filesystem represents either the local Filesystem or the data backed by a project. 43type Filesystem interface { 44 Close() error 45 Open(ctx clingy.Context, loc ulloc.Location, opts *OpenOptions) (ReadHandle, error) 46 Create(ctx clingy.Context, loc ulloc.Location) (WriteHandle, error) 47 Move(ctx clingy.Context, source, dest ulloc.Location) error 48 Remove(ctx context.Context, loc ulloc.Location, opts *RemoveOptions) error 49 List(ctx context.Context, prefix ulloc.Location, opts *ListOptions) (ObjectIterator, error) 50 IsLocalDir(ctx context.Context, loc ulloc.Location) bool 51 Stat(ctx context.Context, loc ulloc.Location) (*ObjectInfo, error) 52} 53 54// 55// object info 56// 57 58// ObjectInfo is a simpler *uplink.Object that contains the minimal information the 59// uplink command needs that multiple types can be converted to. 60type ObjectInfo struct { 61 Loc ulloc.Location 62 IsPrefix bool 63 Created time.Time 64 ContentLength int64 65 Expires time.Time 66 Metadata uplink.CustomMetadata 67} 68 69// uplinkObjectToObjectInfo returns an objectInfo converted from an *uplink.Object. 70func uplinkObjectToObjectInfo(bucket string, obj *uplink.Object) ObjectInfo { 71 return ObjectInfo{ 72 Loc: ulloc.NewRemote(bucket, obj.Key), 73 IsPrefix: obj.IsPrefix, 74 Created: obj.System.Created, 75 ContentLength: obj.System.ContentLength, 76 Expires: obj.System.Expires, 77 Metadata: obj.Custom, 78 } 79} 80 81// uplinkUploadInfoToObjectInfo returns an objectInfo converted from an *uplink.Object. 82func uplinkUploadInfoToObjectInfo(bucket string, upl *uplink.UploadInfo) ObjectInfo { 83 return ObjectInfo{ 84 Loc: ulloc.NewRemote(bucket, upl.Key), 85 IsPrefix: upl.IsPrefix, 86 Created: upl.System.Created, 87 ContentLength: upl.System.ContentLength, 88 Expires: upl.System.Expires, 89 Metadata: upl.Custom, 90 } 91} 92 93// 94// read handles 95// 96 97// ReadHandle is something that can be read from. 98type ReadHandle interface { 99 io.Reader 100 io.Closer 101 Info() ObjectInfo 102} 103 104// uplinkReadHandle implements readHandle for *uplink.Downloads. 105type uplinkReadHandle struct { 106 bucket string 107 dl *uplink.Download 108} 109 110// newUplinkReadHandle constructs an *uplinkReadHandle from an *uplink.Download. 111func newUplinkReadHandle(bucket string, dl *uplink.Download) *uplinkReadHandle { 112 return &uplinkReadHandle{ 113 bucket: bucket, 114 dl: dl, 115 } 116} 117 118func (u *uplinkReadHandle) Read(p []byte) (int, error) { return u.dl.Read(p) } 119func (u *uplinkReadHandle) Close() error { return u.dl.Close() } 120func (u *uplinkReadHandle) Info() ObjectInfo { return uplinkObjectToObjectInfo(u.bucket, u.dl.Info()) } 121 122// osReadHandle implements readHandle for *os.Files. 123type osReadHandle struct { 124 raw *os.File 125 info ObjectInfo 126} 127 128// newOsReadHandle constructs an *osReadHandle from an *os.File. 129func newOSReadHandle(fh *os.File) (*osReadHandle, error) { 130 fi, err := fh.Stat() 131 if err != nil { 132 return nil, errs.Wrap(err) 133 } 134 return &osReadHandle{ 135 raw: fh, 136 info: ObjectInfo{ 137 Loc: ulloc.NewLocal(fh.Name()), 138 IsPrefix: false, 139 Created: fi.ModTime(), // TODO: os specific crtime 140 ContentLength: fi.Size(), 141 }, 142 }, nil 143} 144 145func (o *osReadHandle) Read(p []byte) (int, error) { return o.raw.Read(p) } 146func (o *osReadHandle) Close() error { return o.raw.Close() } 147func (o *osReadHandle) Info() ObjectInfo { return o.info } 148 149// genericReadHandle implements readHandle for an io.Reader. 150type genericReadHandle struct{ r io.Reader } 151 152// newGenericReadHandle constructs a *genericReadHandle from any io.Reader. 153func newGenericReadHandle(r io.Reader) *genericReadHandle { 154 return &genericReadHandle{r: r} 155} 156 157func (g *genericReadHandle) Read(p []byte) (int, error) { return g.r.Read(p) } 158func (g *genericReadHandle) Close() error { return nil } 159func (g *genericReadHandle) Info() ObjectInfo { return ObjectInfo{ContentLength: -1} } 160 161// 162// write handles 163// 164 165// WriteHandle is anything that can be written to with commit/abort semantics. 166type WriteHandle interface { 167 io.Writer 168 Commit() error 169 Abort() error 170} 171 172// uplinkWriteHandle implements writeHandle for *uplink.Uploads. 173type uplinkWriteHandle uplink.Upload 174 175// newUplinkWriteHandle constructs an *uplinkWriteHandle from an *uplink.Upload. 176func newUplinkWriteHandle(dl *uplink.Upload) *uplinkWriteHandle { 177 return (*uplinkWriteHandle)(dl) 178} 179 180func (u *uplinkWriteHandle) raw() *uplink.Upload { 181 return (*uplink.Upload)(u) 182} 183 184func (u *uplinkWriteHandle) Write(p []byte) (int, error) { return u.raw().Write(p) } 185func (u *uplinkWriteHandle) Commit() error { return u.raw().Commit() } 186func (u *uplinkWriteHandle) Abort() error { return u.raw().Abort() } 187 188// osWriteHandle implements writeHandle for *os.Files. 189type osWriteHandle struct { 190 fh *os.File 191 done bool 192} 193 194// newOSWriteHandle constructs an *osWriteHandle from an *os.File. 195func newOSWriteHandle(fh *os.File) *osWriteHandle { 196 return &osWriteHandle{fh: fh} 197} 198 199func (o *osWriteHandle) Write(p []byte) (int, error) { return o.fh.Write(p) } 200 201func (o *osWriteHandle) Commit() error { 202 if o.done { 203 return nil 204 } 205 o.done = true 206 207 return o.fh.Close() 208} 209 210func (o *osWriteHandle) Abort() error { 211 if o.done { 212 return nil 213 } 214 o.done = true 215 216 return errs.Combine( 217 o.fh.Close(), 218 os.Remove(o.fh.Name()), 219 ) 220} 221 222// genericWriteHandle implements writeHandle for an io.Writer. 223type genericWriteHandle struct{ w io.Writer } 224 225// newGenericWriteHandle constructs a *genericWriteHandle from an io.Writer. 226func newGenericWriteHandle(w io.Writer) *genericWriteHandle { 227 return &genericWriteHandle{w: w} 228} 229 230func (g *genericWriteHandle) Write(p []byte) (int, error) { return g.w.Write(p) } 231func (g *genericWriteHandle) Commit() error { return nil } 232func (g *genericWriteHandle) Abort() error { return nil } 233 234// 235// object iteration 236// 237 238// ObjectIterator is an interface type for iterating over objectInfo values. 239type ObjectIterator interface { 240 Next() bool 241 Err() error 242 Item() ObjectInfo 243} 244 245// filteredObjectIterator removes any iteration entries that do not begin with the filter. 246// all entries must begin with the trim string which is removed before checking for the 247// filter. 248type filteredObjectIterator struct { 249 trim ulloc.Location 250 filter ulloc.Location 251 iter ObjectIterator 252} 253 254func (f *filteredObjectIterator) Next() bool { 255 for { 256 if !f.iter.Next() { 257 return false 258 } 259 loc := f.iter.Item().Loc 260 if !loc.HasPrefix(f.trim) { 261 return false 262 } 263 if loc.HasPrefix(f.filter.AsDirectoryish()) || loc == f.filter { 264 return true 265 } 266 } 267} 268 269func (f *filteredObjectIterator) Err() error { return f.iter.Err() } 270 271func (f *filteredObjectIterator) Item() ObjectInfo { 272 item := f.iter.Item() 273 item.Loc = item.Loc.RemovePrefix(f.trim) 274 return item 275} 276 277// emptyObjectIterator is an objectIterator that has no objects. 278type emptyObjectIterator struct{} 279 280func (emptyObjectIterator) Next() bool { return false } 281func (emptyObjectIterator) Err() error { return nil } 282func (emptyObjectIterator) Item() ObjectInfo { return ObjectInfo{} } 283