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 }