github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libfs/file_info.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 "context" 9 "errors" 10 "os" 11 "time" 12 13 "github.com/keybase/client/go/kbfs/data" 14 "github.com/keybase/client/go/kbfs/libkbfs" 15 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 // ErrUnknownPrefetchStatus is returned when the prefetch status given by the 19 // KBFSOps's NodeMetadata is invalid. 20 var ErrUnknownPrefetchStatus = errors.New( 21 "Failed to determine prefetch status") 22 23 // FileInfo is a wrapper around libkbfs.EntryInfo that implements the 24 // os.FileInfo interface. 25 type FileInfo struct { 26 fs *FS 27 ei data.EntryInfo 28 node libkbfs.Node 29 name string 30 } 31 32 var _ os.FileInfo = (*FileInfo)(nil) 33 34 // Name implements the os.FileInfo interface for FileInfo. 35 func (fi *FileInfo) Name() string { 36 return fi.name 37 } 38 39 // Size implements the os.FileInfo interface for FileInfo. 40 func (fi *FileInfo) Size() int64 { 41 // TODO: deal with overflow? 42 return int64(fi.ei.Size) 43 } 44 45 // Mode implements the os.FileInfo interface for FileInfo. 46 func (fi *FileInfo) Mode() os.FileMode { 47 mode, err := WritePermMode( 48 fi.fs.ctx, fi.node, os.FileMode(0), fi.fs.config.KBPKI(), 49 fi.fs.config, fi.fs.h) 50 if err != nil { 51 fi.fs.log.CWarningf( 52 fi.fs.ctx, "Couldn't get mode for file %s: %+v", fi.Name(), err) 53 mode = os.FileMode(0) 54 } 55 56 mode |= 0400 57 switch fi.ei.Type { 58 case data.Dir: 59 mode |= os.ModeDir | 0100 60 case data.Sym: 61 mode |= os.ModeSymlink 62 case data.Exec: 63 mode |= 0100 64 } 65 return mode 66 } 67 68 // ModTime implements the os.FileInfo interface for FileInfo. 69 func (fi *FileInfo) ModTime() time.Time { 70 return time.Unix(0, fi.ei.Mtime) 71 } 72 73 // IsDir implements the os.FileInfo interface for FileInfo. 74 func (fi *FileInfo) IsDir() bool { 75 return fi.ei.Type == data.Dir 76 } 77 78 // KBFSMetadataForSimpleFS contains the KBFS metadata needed to answer a 79 // simpleFSStat call. 80 type KBFSMetadataForSimpleFS struct { 81 LastWriter keybase1.User 82 PrefetchStatus keybase1.PrefetchStatus 83 PrefetchProgress libkbfs.PrefetchProgress 84 } 85 86 // KBFSMetadataForSimpleFSGetter is an interface for something that can return 87 // the last KBFS writer and prefetch status of a directory entry. 88 type KBFSMetadataForSimpleFSGetter interface { 89 KBFSMetadataForSimpleFS() (KBFSMetadataForSimpleFS, error) 90 } 91 92 // PrevRevisionsGetter is an interface for something that can return 93 // the previous revisions of an entry. 94 type PrevRevisionsGetter interface { 95 PrevRevisions() data.PrevRevisions 96 } 97 98 type fileInfoSys struct { 99 fi *FileInfo 100 } 101 102 var _ KBFSMetadataForSimpleFSGetter = fileInfoSys{} 103 104 func (fis fileInfoSys) KBFSMetadataForSimpleFS() ( 105 KBFSMetadataForSimpleFS, error) { 106 if fis.fi.node == nil { 107 // This won't return any last writer for symlinks themselves. 108 // TODO: if we want symlink last writers, we'll need to add a 109 // new interface to KBFSOps to get them. 110 return KBFSMetadataForSimpleFS{}, nil 111 } 112 md, err := fis.fi.fs.config.KBFSOps().GetNodeMetadata( 113 fis.fi.fs.ctx, fis.fi.node) 114 if err != nil { 115 return KBFSMetadataForSimpleFS{}, err 116 } 117 118 prefetchStatus := md.PrefetchStatus.ToProtocolStatus() 119 status := KBFSMetadataForSimpleFS{PrefetchStatus: prefetchStatus} 120 if md.PrefetchProgress != nil { 121 status.PrefetchProgress = *md.PrefetchProgress 122 } 123 124 lastWriterName := md.LastWriterUnverified 125 if lastWriterName == "" { 126 // This can happen in old, buggy team folders where the writer 127 // isn't properly set. See KBFS-2939. 128 return status, nil 129 } 130 131 _, id, err := fis.fi.fs.config.KBPKI().Resolve( 132 fis.fi.fs.ctx, lastWriterName.String(), 133 fis.fi.fs.config.OfflineAvailabilityForID( 134 fis.fi.fs.root.GetFolderBranch().Tlf)) 135 if err != nil { 136 return KBFSMetadataForSimpleFS{}, err 137 } 138 uid, err := id.AsUser() 139 if err != nil { 140 return KBFSMetadataForSimpleFS{}, err 141 } 142 143 status.LastWriter = keybase1.User{ 144 Uid: uid, 145 Username: lastWriterName.String(), 146 } 147 return status, nil 148 } 149 150 var _ PrevRevisionsGetter = fileInfoSys{} 151 152 func (fis fileInfoSys) PrevRevisions() (revs data.PrevRevisions) { 153 return fis.fi.ei.PrevRevisions 154 } 155 156 func (fis fileInfoSys) EntryInfo() data.EntryInfo { 157 return fis.fi.ei 158 } 159 160 // Sys implements the os.FileInfo interface for FileInfo. 161 func (fi *FileInfo) Sys() interface{} { 162 return fileInfoSys{fi} 163 } 164 165 // FileInfoFast always returns a returns a read-only mode, and doesn't populate 166 // LastWriterUnverified. This allows us to avoid doing a Lookup on the entry, 167 // which makes a big difference in ReadDir. 168 type FileInfoFast struct { 169 ei data.EntryInfo 170 name string 171 } 172 173 // Name implements the os.FileInfo interface. 174 func (fif *FileInfoFast) Name() string { 175 return fif.name 176 } 177 178 // Size implements the os.FileInfo interface. 179 func (fif *FileInfoFast) Size() int64 { 180 // TODO: deal with overflow? 181 return int64(fif.ei.Size) 182 } 183 184 // Mode implements the os.FileInfo interface. 185 func (fif *FileInfoFast) Mode() os.FileMode { 186 mode := os.FileMode(0400) 187 switch fif.ei.Type { 188 case data.Dir: 189 mode |= os.ModeDir | 0100 190 case data.Sym: 191 mode |= os.ModeSymlink 192 case data.Exec: 193 mode |= 0100 194 } 195 return mode 196 } 197 198 // ModTime implements the os.FileInfo interface. 199 func (fif *FileInfoFast) ModTime() time.Time { 200 return time.Unix(0, fif.ei.Mtime) 201 } 202 203 // IsDir implements the os.FileInfo interface. 204 func (fif *FileInfoFast) IsDir() bool { 205 return fif.ei.Type == data.Dir 206 } 207 208 // Sys implements the os.FileInfo interface. 209 func (fif *FileInfoFast) Sys() interface{} { 210 return fif 211 } 212 213 type ctxFastModeKey struct{} 214 215 // EnableFastMode returns a context.Context based on ctx that will test to true 216 // with IsFastModeEnabled. 217 func EnableFastMode(ctx context.Context) context.Context { 218 return context.WithValue(ctx, ctxFastModeKey{}, true) 219 } 220 221 // IsFastModeEnabled returns true if fast mode should be enabled. In fast mode, 222 // *FS doesn't populate LastWriterUnverified, and always returns read-only 223 // info. All *FS created under this ctx will also be in fast mode. 224 func IsFastModeEnabled(ctx context.Context) bool { 225 v, ok := ctx.Value(ctxFastModeKey{}).(bool) 226 return ok && v 227 }