github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libdokan/common.go (about)

     1  // Copyright 2016 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 libdokan
     6  
     7  import (
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/keybase/client/go/kbfs/data"
    12  	"github.com/keybase/client/go/kbfs/dokan"
    13  	"github.com/keybase/client/go/kbfs/idutil"
    14  	"github.com/keybase/client/go/kbfs/kbfsmd"
    15  	"golang.org/x/net/context"
    16  )
    17  
    18  const (
    19  	// PublicName is the name of the parent of all public top-level folders.
    20  	PublicName = "public"
    21  
    22  	// PrivateName is the name of the parent of all private top-level folders.
    23  	PrivateName = "private"
    24  
    25  	// TeamName is the name of the parent of all team top-level folders.
    26  	TeamName = "team"
    27  
    28  	// CtxOpID is the display name for the unique operation Dokan ID tag.
    29  	CtxOpID = "DID"
    30  
    31  	// WrongUserErrorFileName is the name of error directory for other users.
    32  	WrongUserErrorFileName = `kbfs.access.denied.for.other.windows.users.txt`
    33  
    34  	// WrongUserErrorContents is the contents of the file.
    35  	WrongUserErrorContents = `Access to KBFS is limited to the windows user (sid) running KBFS.`
    36  )
    37  
    38  // CtxTagKey is the type used for unique context tags
    39  type CtxTagKey int
    40  
    41  const (
    42  	// CtxIDKey is the type of the tag for unique operation IDs.
    43  	CtxIDKey CtxTagKey = iota
    44  )
    45  
    46  // eiToStat converts from a libkbfs.EntryInfo and error to a *dokan.Stat and error.
    47  // Note that handling symlinks to directories requires extra processing not done here.
    48  func eiToStat(ei data.EntryInfo, err error) (*dokan.Stat, error) {
    49  	if err != nil {
    50  		return nil, errToDokan(err)
    51  	}
    52  	st := &dokan.Stat{}
    53  	fillStat(st, &ei)
    54  	return st, nil
    55  }
    56  
    57  // fillStat fill a dokan.Stat from a libkbfs.DirEntry.
    58  // Note that handling symlinks to directories requires extra processing not done here.
    59  func fillStat(a *dokan.Stat, de *data.EntryInfo) {
    60  	a.FileSize = int64(de.Size)
    61  	a.LastWrite = time.Unix(0, de.Mtime)
    62  	a.LastAccess = a.LastWrite
    63  	a.Creation = time.Unix(0, de.Ctime)
    64  	switch de.Type {
    65  	case data.File, data.Exec:
    66  		a.FileAttributes = dokan.FileAttributeNormal
    67  	case data.Dir:
    68  		a.FileAttributes = dokan.FileAttributeDirectory
    69  	case data.Sym:
    70  		a.FileAttributes = dokan.FileAttributeReparsePoint
    71  		a.ReparsePointTag = dokan.IOReparseTagSymlink
    72  	}
    73  }
    74  
    75  // addFileAttribute adds a file attribute to the stat struct.
    76  func addFileAttribute(a *dokan.Stat, fa dokan.FileAttribute) {
    77  	// FileAttributeNormal is valid only if no other attribute is set.
    78  	// Thus clear the normal flag (if set) from the attributes and or
    79  	// the new flag.
    80  	a.FileAttributes = (a.FileAttributes &^ dokan.FileAttributeNormal) | fa
    81  }
    82  
    83  // errToDokan makes some libkbfs errors easier to digest in dokan. Not needed in most places.
    84  func errToDokan(err error) error {
    85  	switch err.(type) {
    86  	case idutil.NoSuchNameError:
    87  		return dokan.ErrObjectNameNotFound
    88  	case idutil.NoSuchUserError:
    89  		return dokan.ErrObjectNameNotFound
    90  	case kbfsmd.ServerErrorUnauthorized:
    91  		return dokan.ErrAccessDenied
    92  	case nil:
    93  		return nil
    94  	}
    95  	return err
    96  }
    97  
    98  // defaultDirectoryInformation returns default directory information.
    99  func defaultDirectoryInformation() (*dokan.Stat, error) {
   100  	var st dokan.Stat
   101  	st.FileAttributes = dokan.FileAttributeDirectory
   102  	return &st, nil
   103  }
   104  
   105  // defaultFileInformation returns default file information.
   106  func defaultFileInformation() (*dokan.Stat, error) {
   107  	var st dokan.Stat
   108  	st.FileAttributes = dokan.FileAttributeNormal
   109  	return &st, nil
   110  }
   111  
   112  // defaultSymlinkFileInformation returns default symlink to file information.
   113  func defaultSymlinkFileInformation() (*dokan.Stat, error) {
   114  	var st dokan.Stat
   115  	st.FileAttributes = dokan.FileAttributeReparsePoint
   116  	st.ReparsePointTag = dokan.IOReparseTagSymlink
   117  	return &st, nil
   118  }
   119  
   120  // defaultSymlinkDirInformation returns default symlink to directory information.
   121  func defaultSymlinkDirInformation() (*dokan.Stat, error) {
   122  	var st dokan.Stat
   123  	st.FileAttributes = dokan.FileAttributeReparsePoint | dokan.FileAttributeDirectory
   124  	st.ReparsePointTag = dokan.IOReparseTagSymlink
   125  	return &st, nil
   126  }
   127  
   128  // lowerTranslateCandidate returns whether a path components
   129  // has a (different) lowercase translation.
   130  func lowerTranslateCandidate(oc *openContext, s string) string {
   131  	if !oc.isUppercasePath {
   132  		return ""
   133  	}
   134  	c := strings.ToLower(s)
   135  	if c == s {
   136  		return ""
   137  	}
   138  	return c
   139  }
   140  
   141  func stringReadFile(contents string) dokan.File {
   142  	return &stringFile{data: contents}
   143  }
   144  
   145  type stringFile struct {
   146  	emptyFile
   147  	data string
   148  }
   149  
   150  // GetFileInformation does stats for dokan.
   151  func (s *stringFile) GetFileInformation(ctx context.Context, fi *dokan.FileInfo) (*dokan.Stat, error) {
   152  	a, err := defaultFileInformation()
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	a.FileAttributes |= dokan.FileAttributeReadonly
   157  	a.FileSize = int64(len(s.data))
   158  	t := time.Now()
   159  	a.LastWrite = t
   160  	a.LastAccess = t
   161  	a.Creation = t
   162  	return a, nil
   163  }
   164  
   165  // ReadFile does reads for dokan.
   166  func (s *stringFile) ReadFile(ctx context.Context, fi *dokan.FileInfo, bs []byte, offset int64) (int, error) {
   167  	data := s.data
   168  	if offset >= int64(len(data)) {
   169  		return 0, nil
   170  	}
   171  
   172  	data = data[int(offset):]
   173  
   174  	return copy(bs, data), nil
   175  }