1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
15 "git.curoverse.com/arvados.git/sdk/go/manifest"
25 type keepClient interface {
26 ManifestFileReader(manifest.Manifest, string) (File, error)
29 type collectionFile struct {
31 collection *Collection
36 func (cf *collectionFile) Size() int64 {
40 func (cf *collectionFile) Readdir(count int) ([]os.FileInfo, error) {
44 func (cf *collectionFile) Stat() (os.FileInfo, error) {
45 return collectionDirent{
46 collection: cf.collection,
53 type collectionDir struct {
54 collection *Collection
59 // Readdir implements os.File.
60 func (cd *collectionDir) Readdir(count int) ([]os.FileInfo, error) {
65 } else if len(ret) == 0 {
69 if count >= len(ret) {
73 cd.dirents = cd.dirents[count:]
74 return ret[:count], err
77 // Stat implements os.File.
78 func (cd *collectionDir) Stat() (os.FileInfo, error) {
79 return collectionDirent{
80 collection: cd.collection,
81 name: path.Base(cd.stream),
83 size: int64(len(cd.dirents)),
87 // Close implements os.File.
88 func (cd *collectionDir) Close() error {
92 // Read implements os.File.
93 func (cd *collectionDir) Read([]byte) (int, error) {
97 // Seek implements os.File.
98 func (cd *collectionDir) Seek(int64, int) (int64, error) {
102 // collectionDirent implements os.FileInfo.
103 type collectionDirent struct {
104 collection *Collection
111 // Name implements os.FileInfo.
112 func (e collectionDirent) Name() string {
116 // ModTime implements os.FileInfo.
117 func (e collectionDirent) ModTime() time.Time {
118 if e.collection.ModifiedAt == nil {
121 return *e.collection.ModifiedAt
124 // Mode implements os.FileInfo.
125 func (e collectionDirent) Mode() os.FileMode {
133 // IsDir implements os.FileInfo.
134 func (e collectionDirent) IsDir() bool {
138 // Size implements os.FileInfo.
139 func (e collectionDirent) Size() int64 {
143 // Sys implements os.FileInfo.
144 func (e collectionDirent) Sys() interface{} {
148 // collectionFS implements http.FileSystem.
149 type collectionFS struct {
150 collection *Collection
155 // FileSystem returns an http.FileSystem for the collection.
156 func (c *Collection) FileSystem(client *Client, kc keepClient) http.FileSystem {
157 return &collectionFS{
164 func (c *collectionFS) Open(name string) (http.File, error) {
165 // Ensure name looks the way it does in a manifest.
166 name = path.Clean("/" + name)
167 if name == "/" || name == "./" {
169 } else if strings.HasPrefix(name, "/") {
173 m := manifest.Manifest{Text: c.collection.ManifestText}
175 filesizes := c.fileSizes()
177 // Return a file if it exists.
178 if size, ok := filesizes[name]; ok {
179 reader, err := c.kc.ManifestFileReader(m, name)
183 return &collectionFile{
185 collection: c.collection,
186 name: path.Base(name),
191 // Return a directory if it's the root dir or there are file
193 children := map[string]collectionDirent{}
194 for fnm, size := range filesizes {
195 if !strings.HasPrefix(fnm, name+"/") {
199 ent := fnm[len(name)+1:]
200 if i := strings.Index(ent, "/"); i >= 0 {
205 e.collection = c.collection
211 if len(children) == 0 && name != "." {
212 return nil, os.ErrNotExist
214 dirents := make([]os.FileInfo, 0, len(children))
215 for _, ent := range children {
216 dirents = append(dirents, ent)
218 return &collectionDir{
219 collection: c.collection,
225 // fileSizes returns a map of files that can be opened. Each key
227 func (c *collectionFS) fileSizes() map[string]int64 {
228 var sizes map[string]int64
229 m := manifest.Manifest{Text: c.collection.ManifestText}
230 for ms := range m.StreamIter() {
231 for _, fss := range ms.FileStreamSegments {
233 sizes = map[string]int64{}
235 sizes[ms.StreamName+"/"+fss.Name] += int64(fss.SegLen)