github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libdokan/file.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 "github.com/keybase/client/go/kbfs/data" 9 "github.com/keybase/client/go/kbfs/dokan" 10 "github.com/keybase/client/go/kbfs/libkbfs" 11 "github.com/keybase/client/go/libkb" 12 "golang.org/x/net/context" 13 ) 14 15 // File represents KBFS files. 16 type File struct { 17 FSO 18 } 19 20 func newFile(folder *Folder, node libkbfs.Node, name string, parent libkbfs.Node) *File { 21 f := &File{FSO{ 22 name: name, 23 parent: parent, 24 folder: folder, 25 node: node, 26 }} 27 f.refcount.Increase() 28 return f 29 } 30 31 // GetFileInformation for dokan. 32 func (f *File) GetFileInformation(ctx context.Context, fi *dokan.FileInfo) (a *dokan.Stat, err error) { 33 f.folder.fs.logEnter(ctx, "File GetFileInformation") 34 defer func() { f.folder.reportErr(ctx, libkbfs.ReadMode, err) }() 35 36 a, err = eiToStat(f.folder.fs.config.KBFSOps().Stat(ctx, f.node)) 37 if a != nil { 38 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File GetFileInformation node=%v => %v", f.node, *a) 39 } else { 40 f.folder.fs.log.CDebugf(ctx, "File GetFileInformation node=%v => Error %T %v", f.node, err, err) 41 } 42 return a, err 43 } 44 45 func (f *File) namePPS() data.PathPartString { 46 return f.parent.ChildName(f.name) 47 } 48 49 // CanDeleteFile - return just nil 50 // TODO check for permissions here. 51 func (f *File) CanDeleteFile(ctx context.Context, fi *dokan.FileInfo) error { 52 f.folder.fs.logEnterf(ctx, "File CanDeleteFile for %s", f.namePPS()) 53 return nil 54 } 55 56 // Cleanup - for dokan, remember to handle deletions. 57 // If Cleanup is called with non-nil FileInfo that has IsDeleteOnClose() 58 // no libdokan locks should be held prior to the call. 59 func (f *File) Cleanup(ctx context.Context, fi *dokan.FileInfo) { 60 var err error 61 f.folder.fs.logEnter(ctx, "File Cleanup") 62 defer func() { f.folder.reportErr(ctx, libkbfs.WriteMode, err) }() 63 64 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "Cleanup %v", *f) 65 if fi != nil && fi.IsDeleteOnClose() { 66 // renameAndDeletionLock should be the first lock to be grabbed in libdokan. 67 f.folder.fs.renameAndDeletionLock.Lock() 68 defer f.folder.fs.renameAndDeletionLock.Unlock() 69 namePPS := f.namePPS() 70 f.folder.fs.vlog.CLogf( 71 ctx, libkb.VLog1, "Removing (Delete) file in cleanup %s", namePPS) 72 73 err = f.folder.fs.config.KBFSOps().RemoveEntry( 74 ctx, f.parent, namePPS) 75 } 76 77 if f.refcount.Decrease() { 78 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "Forgetting file node") 79 f.folder.forgetNode(ctx, f.node) 80 } 81 } 82 83 // FlushFileBuffers performs a (f)sync. 84 func (f *File) FlushFileBuffers(ctx context.Context, fi *dokan.FileInfo) (err error) { 85 f.folder.fs.logEnter(ctx, "File FlushFileBuffers") 86 defer func() { f.folder.reportErr(ctx, libkbfs.WriteMode, err) }() 87 88 return f.folder.fs.config.KBFSOps().SyncAll(ctx, f.node.GetFolderBranch()) 89 } 90 91 // ReadFile for dokan reads. 92 func (f *File) ReadFile(ctx context.Context, fi *dokan.FileInfo, bs []byte, offset int64) (n int, err error) { 93 f.folder.fs.logEnter(ctx, "ReadFile") 94 defer func() { f.folder.reportErr(ctx, libkbfs.ReadMode, err) }() 95 96 var nlarge int64 97 nlarge, err = f.folder.fs.config.KBFSOps().Read(ctx, f.node, bs, offset) 98 99 // This is safe since length of slices always fits into an int 100 return int(nlarge), err 101 } 102 103 // WriteFile for dokan writes. 104 func (f *File) WriteFile(ctx context.Context, fi *dokan.FileInfo, bs []byte, offset int64) (n int, err error) { 105 f.folder.fs.logEnter(ctx, "WriteFile") 106 defer func() { f.folder.reportErr(ctx, libkbfs.WriteMode, err) }() 107 108 if offset == -1 { 109 ei, err := f.folder.fs.config.KBFSOps().Stat(ctx, f.node) 110 if err != nil { 111 return 0, err 112 } 113 offset = int64(ei.Size) 114 } 115 116 err = f.folder.fs.config.KBFSOps().Write(ctx, f.node, bs, offset) 117 return len(bs), err 118 } 119 120 // SetEndOfFile for dokan (f)truncates. 121 func (f *File) SetEndOfFile(ctx context.Context, fi *dokan.FileInfo, length int64) (err error) { 122 f.folder.fs.logEnter(ctx, "File SetEndOfFile") 123 defer func() { f.folder.reportErr(ctx, libkbfs.WriteMode, err) }() 124 125 return f.folder.fs.config.KBFSOps().Truncate(ctx, f.node, uint64(length)) 126 } 127 128 // SetAllocationSize for dokan (f)truncates but does not grow 129 // file size (it may fallocate, but that is not done at the 130 // moment). 131 func (f *File) SetAllocationSize(ctx context.Context, fi *dokan.FileInfo, newSize int64) (err error) { 132 f.folder.fs.logEnter(ctx, "File SetAllocationSize") 133 defer func() { f.folder.reportErr(ctx, libkbfs.WriteMode, err) }() 134 135 ei, err := f.folder.fs.config.KBFSOps().Stat(ctx, f.node) 136 if err != nil { 137 return err 138 } 139 140 // Refuse to grow the file. 141 if int64(ei.Size) <= newSize { 142 return nil 143 } 144 145 return f.folder.fs.config.KBFSOps().Truncate(ctx, f.node, uint64(newSize)) 146 } 147 148 // SetFileAttributes for Dokan. 149 func (f *File) SetFileAttributes(ctx context.Context, fi *dokan.FileInfo, fileAttributes dokan.FileAttribute) error { 150 f.folder.fs.logEnterf(ctx, "File SetFileAttributes %X", fileAttributes) 151 // TODO handle attributes for real. 152 return nil 153 }