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  }