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