github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/fuse/file.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package fuse 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "time" 11 12 fuselib "bazil.org/fuse" 13 "golang.org/x/net/context" 14 15 "github.com/swiftstack/ProxyFS/blunder" 16 "github.com/swiftstack/ProxyFS/fs" 17 "github.com/swiftstack/ProxyFS/inode" 18 ) 19 20 type File struct { 21 volumeHandle fs.VolumeHandle 22 inodeNumber inode.InodeNumber 23 } 24 25 func (f File) Access(ctx context.Context, req *fuselib.AccessRequest) (err error) { 26 enterGate() 27 defer leaveGate() 28 29 if f.volumeHandle.Access(inode.InodeUserID(req.Uid), inode.InodeGroupID(req.Gid), nil, f.inodeNumber, inode.InodeMode(req.Mask)) { 30 err = nil 31 } else { 32 err = newFuseError(blunder.NewError(blunder.PermDeniedError, "EACCES")) 33 } 34 35 return 36 } 37 38 func (f File) Attr(ctx context.Context, attr *fuselib.Attr) (err error) { 39 var ( 40 stat fs.Stat 41 ) 42 43 enterGate() 44 defer leaveGate() 45 46 stat, err = f.volumeHandle.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, f.inodeNumber) 47 if nil != err { 48 err = newFuseError(err) 49 return 50 } 51 if uint64(inode.FileType) != stat[fs.StatFType] { 52 err = fmt.Errorf("[fuse]Dir.Attr() called on non-File") 53 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 54 err = newFuseError(err) 55 return 56 } 57 58 attr.Valid = time.Duration(time.Microsecond) // TODO: Make this settable if FUSE inside ProxyFS endures 59 attr.Inode = uint64(f.inodeNumber) // or stat[fs.StatINum] 60 attr.Size = stat[fs.StatSize] 61 attr.Blocks = (stat[fs.StatSize] + 511) / 512 62 attr.Atime = time.Unix(0, int64(stat[fs.StatATime])) 63 attr.Mtime = time.Unix(0, int64(stat[fs.StatMTime])) 64 attr.Ctime = time.Unix(0, int64(stat[fs.StatCTime])) 65 attr.Crtime = time.Unix(0, int64(stat[fs.StatCRTime])) 66 attr.Mode = os.FileMode(stat[fs.StatMode] & 0777) 67 attr.Nlink = uint32(stat[fs.StatNLink]) 68 attr.Uid = uint32(stat[fs.StatUserID]) 69 attr.Gid = uint32(stat[fs.StatGroupID]) 70 attr.BlockSize = 4096 // Just a guess at a reasonable block size 71 72 return 73 } 74 75 func (f File) Setattr(ctx context.Context, req *fuselib.SetattrRequest, resp *fuselib.SetattrResponse) (err error) { 76 var ( 77 stat fs.Stat 78 statUpdates fs.Stat 79 ) 80 81 enterGate() 82 defer leaveGate() 83 84 stat, err = f.volumeHandle.Getstat(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber) 85 if nil != err { 86 err = newFuseError(err) 87 return 88 } 89 if uint64(inode.FileType) != stat[fs.StatFType] { 90 err = fmt.Errorf("[fuse]Dir.Attr() called on non-File") 91 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 92 err = newFuseError(err) 93 return 94 } 95 96 statUpdates = make(fs.Stat) 97 98 if 0 != (fuselib.SetattrMode & req.Valid) { 99 statUpdates[fs.StatMode] = uint64(req.Mode & 0777) 100 } 101 if 0 != (fuselib.SetattrUid & req.Valid) { 102 statUpdates[fs.StatUserID] = uint64(req.Uid) 103 } 104 if 0 != (fuselib.SetattrGid & req.Valid) { 105 statUpdates[fs.StatGroupID] = uint64(req.Gid) 106 } 107 if 0 != (fuselib.SetattrAtime & req.Valid) { 108 statUpdates[fs.StatATime] = uint64(req.Atime.UnixNano()) 109 } 110 if 0 != (fuselib.SetattrMtime & req.Valid) { 111 statUpdates[fs.StatMTime] = uint64(req.Mtime.UnixNano()) 112 } 113 if 0 != (fuselib.SetattrAtimeNow & req.Valid) { 114 statUpdates[fs.StatATime] = uint64(time.Now().UnixNano()) 115 } 116 if 0 != (fuselib.SetattrMtimeNow & req.Valid) { 117 statUpdates[fs.StatMTime] = uint64(time.Now().UnixNano()) 118 } 119 if 0 != (fuselib.SetattrCrtime & req.Valid) { 120 statUpdates[fs.StatCRTime] = uint64(req.Crtime.UnixNano()) 121 } 122 123 err = f.volumeHandle.Setstat(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber, statUpdates) 124 if nil != err { 125 err = newFuseError(err) 126 return 127 } 128 129 if 0 != (fuselib.SetattrSize & req.Valid) { 130 err = f.volumeHandle.Resize(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber, req.Size) 131 if nil != err { 132 err = newFuseError(err) 133 return 134 } 135 } 136 137 return 138 } 139 140 // Flush is called by Fuse when the VFS layer calls the fuse drivers flush() 141 // routine. According to some documentation, this is called as a result of 142 // a close() on a file descriptor: 143 // https://dri.freedesktop.org/docs/drm/filesystems/vfs.html#id2 144 // 145 func (f File) Flush(ctx context.Context, req *fuselib.FlushRequest) (err error) { 146 enterGate() 147 defer leaveGate() 148 149 // there's no flushing necessary for a close() 150 return 151 } 152 153 func (f File) Fsync(ctx context.Context, req *fuselib.FsyncRequest) (err error) { 154 enterGate() 155 defer leaveGate() 156 157 err = f.volumeHandle.Flush(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber) 158 if nil != err { 159 err = newFuseError(err) 160 } 161 return 162 } 163 164 func (f File) Read(ctx context.Context, req *fuselib.ReadRequest, resp *fuselib.ReadResponse) (err error) { 165 enterGate() 166 defer leaveGate() 167 168 buf, err := f.volumeHandle.Read(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber, uint64(req.Offset), uint64(req.Size), nil) 169 if err != nil && err != io.EOF { 170 err = newFuseError(err) 171 return 172 } 173 resp.Data = buf 174 err = nil 175 return 176 } 177 178 func (f File) Write(ctx context.Context, req *fuselib.WriteRequest, resp *fuselib.WriteResponse) (err error) { 179 enterGate() 180 defer leaveGate() 181 182 // We need to buffer contents of req.Data because fs.Write() will likely retain a reference to it 183 // (down in the Chunked PUT retry buffer) and Bazil FUSE will be reusing this WriteRequest (including 184 // its .Data buf) for the next request. 185 186 bufferedData := make([]byte, len(req.Data), len(req.Data)) 187 copy(bufferedData, req.Data) 188 189 size, err := f.volumeHandle.Write(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, f.inodeNumber, uint64(req.Offset), bufferedData, nil) 190 if nil == err { 191 resp.Size = int(size) 192 } else { 193 err = newFuseError(err) 194 } 195 return 196 }