github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfs/httpfs.go (about) 1 // Copyright 2018 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libfs 6 7 import ( 8 "net/http" 9 "os" 10 11 "github.com/keybase/client/go/kbfs/data" 12 "github.com/keybase/client/go/kbfs/libkbfs" 13 "github.com/pkg/errors" 14 ) 15 16 type dir struct { 17 fs *FS 18 dirname string 19 node libkbfs.Node 20 } 21 22 // Readdir reads children from d. 23 func (d *dir) Readdir(count int) (fis []os.FileInfo, err error) { 24 d.fs.log.CDebugf(d.fs.ctx, "ReadDir %s", count) 25 defer func() { 26 d.fs.deferLog.CDebugf(d.fs.ctx, "ReadDir done: %+v", err) 27 err = translateErr(err) 28 }() 29 30 return d.fs.readDir(d.node) 31 } 32 33 // fileOrDir is a wrapper around billy FS types that satisfies http.File, which 34 // is either a file or a dir. 35 type fileOrDir struct { 36 file *File 37 dir *dir 38 ei data.EntryInfo 39 } 40 41 var _ http.File = fileOrDir{} 42 43 // fileOrDir implements the http.File interface. 44 func (fod fileOrDir) Read(p []byte) (n int, err error) { 45 defer func() { 46 if err != nil { 47 err = translateErr(err) 48 } 49 }() 50 if fod.file == nil { 51 return 0, libkbfs.NotFileError{} 52 } 53 return fod.file.Read(p) 54 } 55 56 // Close implements the http.File interface. 57 func (fod fileOrDir) Close() (err error) { 58 defer func() { 59 if err != nil { 60 err = translateErr(err) 61 } 62 }() 63 if fod.file != nil { 64 err = fod.file.Close() 65 } 66 if fod.dir != nil { 67 fod.dir.node = nil 68 } 69 fod.file = nil 70 fod.dir = nil 71 return err 72 } 73 74 // Seek implements the http.File interface. 75 func (fod fileOrDir) Seek(offset int64, whence int) (n int64, err error) { 76 defer func() { 77 if err != nil { 78 err = translateErr(err) 79 } 80 }() 81 if fod.file == nil { 82 return 0, libkbfs.NotFileError{} 83 } 84 return fod.file.Seek(offset, whence) 85 } 86 87 // Readdir implements the http.File interface. 88 func (fod fileOrDir) Readdir(count int) (fis []os.FileInfo, err error) { 89 defer func() { 90 if err != nil { 91 err = translateErr(err) 92 } 93 }() 94 if fod.dir == nil { 95 return nil, libkbfs.NotDirError{} 96 } 97 return fod.dir.Readdir(count) 98 } 99 100 // Stat implements the http.File interface. 101 func (fod fileOrDir) Stat() (fi os.FileInfo, err error) { 102 defer func() { 103 if err != nil { 104 err = translateErr(err) 105 } 106 }() 107 if fod.file != nil { 108 return &FileInfo{ 109 fs: fod.file.fs, 110 ei: fod.ei, 111 node: fod.file.node, 112 name: fod.file.node.GetBasename().Plaintext(), 113 }, nil 114 } else if fod.dir != nil { 115 return &FileInfo{ 116 fs: fod.dir.fs, 117 ei: fod.ei, 118 node: fod.dir.node, 119 name: fod.dir.node.GetBasename().Plaintext(), 120 }, nil 121 } 122 return nil, errors.New("invalid fod") 123 } 124 125 // httpFileSystem is a simple wrapper around *FS that satisfies http.FileSystem 126 // interface. 127 type httpFileSystem struct { 128 fs *FS 129 } 130 131 var _ http.FileSystem = httpFileSystem{} 132 133 // Open implements the http.FileSystem interface. 134 func (hfs httpFileSystem) Open(filename string) (entry http.File, err error) { 135 hfs.fs.log.CDebugf( 136 hfs.fs.ctx, "hfs.Open %s", hfs.fs.PathForLogging(filename)) 137 defer func() { 138 hfs.fs.deferLog.CDebugf(hfs.fs.ctx, "hfs.Open done: %+v", err) 139 if err != nil { 140 err = translateErr(err) 141 } 142 }() 143 144 n, ei, err := hfs.fs.lookupOrCreateEntry(filename, os.O_RDONLY, 0600) 145 if err != nil { 146 return fileOrDir{}, err 147 } 148 149 if ei.Type.IsFile() { 150 return fileOrDir{ 151 file: &File{ 152 fs: hfs.fs, 153 filename: n.GetBasename().Plaintext(), 154 node: n, 155 readOnly: true, 156 offset: 0, 157 }, 158 ei: ei, 159 }, nil 160 } 161 return fileOrDir{ 162 dir: &dir{ 163 fs: hfs.fs, 164 dirname: n.GetBasename().Plaintext(), 165 node: n, 166 }, 167 ei: ei, 168 }, nil 169 }