github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/fuse/dir.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 "os" 9 "time" 10 11 fuselib "bazil.org/fuse" 12 fusefslib "bazil.org/fuse/fs" 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 "github.com/swiftstack/ProxyFS/logger" 19 ) 20 21 type Dir struct { 22 volumeHandle fs.VolumeHandle 23 inodeNumber inode.InodeNumber 24 } 25 26 func (d Dir) Access(ctx context.Context, req *fuselib.AccessRequest) error { 27 enterGate() 28 defer leaveGate() 29 30 if d.volumeHandle.Access(inode.InodeUserID(req.Uid), inode.InodeGroupID(req.Gid), nil, d.inodeNumber, inode.InodeMode(req.Mask)) { 31 return nil 32 } else { 33 return newFuseError(blunder.NewError(blunder.PermDeniedError, "EACCES")) 34 } 35 } 36 37 func (d Dir) Attr(ctx context.Context, attr *fuselib.Attr) (err error) { 38 var ( 39 stat fs.Stat 40 ) 41 42 enterGate() 43 defer leaveGate() 44 45 stat, err = d.volumeHandle.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, d.inodeNumber) 46 if nil != err { 47 err = newFuseError(err) 48 return 49 } 50 if uint64(inode.DirType) != stat[fs.StatFType] { 51 err = fmt.Errorf("[fuse]Dir.Attr() called on non-Dir") 52 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 53 err = newFuseError(err) 54 return 55 } 56 57 attr.Valid = time.Duration(time.Microsecond) // TODO: Make this settable if FUSE inside ProxyFS endures 58 attr.Inode = uint64(d.inodeNumber) // or stat[fs.StatINum] 59 attr.Size = stat[fs.StatSize] 60 attr.Atime = time.Unix(0, int64(stat[fs.StatATime])) 61 attr.Mtime = time.Unix(0, int64(stat[fs.StatMTime])) 62 attr.Ctime = time.Unix(0, int64(stat[fs.StatCTime])) 63 attr.Crtime = time.Unix(0, int64(stat[fs.StatCRTime])) 64 attr.Mode = os.ModeDir | os.FileMode(stat[fs.StatMode]&0777) 65 attr.Nlink = uint32(stat[fs.StatNLink]) 66 attr.Uid = uint32(stat[fs.StatUserID]) 67 attr.Gid = uint32(stat[fs.StatGroupID]) 68 69 return 70 } 71 72 func (d Dir) 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 = d.volumeHandle.Getstat(inode.InodeUserID(req.Uid), inode.InodeGroupID(req.Gid), nil, d.inodeNumber) 82 if nil != err { 83 err = newFuseError(err) 84 return 85 } 86 if uint64(inode.DirType) != stat[fs.StatFType] { 87 err = fmt.Errorf("[fuse]Dir.Attr() called on non-Dir") 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 = d.volumeHandle.Setstat(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, statUpdates) 121 if nil != err { 122 err = newFuseError(err) 123 } 124 125 return 126 } 127 128 func (d Dir) Lookup(ctx context.Context, name string) (fusefslib.Node, error) { 129 enterGate() 130 defer leaveGate() 131 132 childInodeNumber, err := d.volumeHandle.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, d.inodeNumber, name) 133 if err != nil { 134 return nil, fuselib.ENOENT 135 } 136 137 isDir, err := d.volumeHandle.IsDir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, childInodeNumber) 138 if isDir { 139 return Dir{volumeHandle: d.volumeHandle, inodeNumber: childInodeNumber}, nil 140 } else if err != nil { 141 err = newFuseError(err) 142 return nil, err 143 } 144 145 isFile, err := d.volumeHandle.IsFile(inode.InodeRootUserID, inode.InodeGroupID(0), nil, childInodeNumber) 146 if isFile { 147 return File{volumeHandle: d.volumeHandle, inodeNumber: childInodeNumber}, nil 148 } else if err != nil { 149 err = newFuseError(err) 150 return nil, err 151 } 152 153 isSymlink, err := d.volumeHandle.IsSymlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, childInodeNumber) 154 if isSymlink { 155 return Symlink{volumeHandle: d.volumeHandle, inodeNumber: childInodeNumber}, nil 156 } else if err != nil { 157 err = newFuseError(err) 158 return nil, err 159 } 160 161 actualType, err := d.volumeHandle.GetType(inode.InodeRootUserID, inode.InodeGroupID(0), nil, childInodeNumber) 162 if err != nil { 163 err = newFuseError(err) 164 return nil, err 165 } else { 166 err = fmt.Errorf("Unrecognized inode type %v", actualType) 167 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 168 err = newFuseError(err) 169 return nil, err 170 } 171 } 172 173 func inodeTypeToDirentType(inodeType inode.InodeType) fuselib.DirentType { 174 switch inodeType { 175 case inode.FileType: 176 return fuselib.DT_File 177 case inode.DirType: 178 return fuselib.DT_Dir 179 case inode.SymlinkType: 180 return fuselib.DT_Link 181 default: 182 return fuselib.DT_Unknown 183 } 184 } 185 186 func (d Dir) ReadDirAll(ctx context.Context) ([]fuselib.Dirent, error) { 187 enterGate() 188 defer leaveGate() 189 190 entries := make([]inode.DirEntry, 0) 191 entryCount := uint64(0) 192 lastEntryName := "" 193 194 more := true 195 for more { 196 var readEntries []inode.DirEntry 197 var readCount uint64 198 var err error 199 200 readEntries, readCount, more, err = d.volumeHandle.Readdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, d.inodeNumber, 1024, lastEntryName) 201 if err != nil { 202 logger.ErrorfWithError(err, "Error in ReadDirAll") 203 return nil, fuselib.EIO 204 } 205 entries = append(entries, readEntries...) 206 entryCount += readCount 207 lastEntryName = readEntries[len(readEntries)-1].Basename 208 } 209 210 fuseEntries := make([]fuselib.Dirent, entryCount) 211 212 for i, entry := range entries { 213 214 fuseEntries[i] = fuselib.Dirent{ 215 Inode: uint64(entry.InodeNumber), 216 Type: inodeTypeToDirentType(entry.Type), 217 Name: entry.Basename, 218 } 219 } 220 return fuseEntries, nil 221 } 222 223 func (d Dir) Remove(ctx context.Context, req *fuselib.RemoveRequest) (err error) { 224 enterGate() 225 defer leaveGate() 226 227 if req.Dir { 228 err = d.volumeHandle.Rmdir(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.Name) 229 } else { 230 err = d.volumeHandle.Unlink(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.Name) 231 } 232 if nil != err { 233 err = newFuseError(err) 234 } 235 return 236 } 237 238 func (d Dir) Mknod(ctx context.Context, req *fuselib.MknodRequest) (fusefslib.Node, error) { 239 enterGate() 240 defer leaveGate() 241 242 // Note: NFSd apparently prefers to use Mknod() instead of Create() when creating normal files... 243 if 0 != (inode.InodeMode(req.Mode) & ^inode.PosixModePerm) { 244 err := fmt.Errorf("Invalid Mode... only normal file creations supported") 245 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 246 err = newFuseError(err) 247 return nil, err 248 } 249 inodeNumber, err := d.volumeHandle.Create(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.Name, inode.InodeMode(req.Mode)) 250 if err != nil { 251 err = newFuseError(err) 252 return nil, err 253 } 254 file := File{volumeHandle: d.volumeHandle, inodeNumber: inodeNumber} 255 return file, nil 256 } 257 258 func (d Dir) Create(ctx context.Context, req *fuselib.CreateRequest, resp *fuselib.CreateResponse) (fusefslib.Node, fusefslib.Handle, error) { 259 enterGate() 260 defer leaveGate() 261 262 if 0 != (inode.InodeMode(req.Mode) & ^inode.PosixModePerm) { 263 err := fmt.Errorf("Invalid Mode... only normal file creations supported") 264 err = blunder.AddError(err, blunder.InvalidInodeTypeError) 265 err = newFuseError(err) 266 return nil, nil, err 267 } 268 inodeNumber, err := d.volumeHandle.Create(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.Name, inode.InodeMode(req.Mode)) 269 if err != nil { 270 err = newFuseError(err) 271 return nil, nil, err 272 } 273 file := File{volumeHandle: d.volumeHandle, inodeNumber: inodeNumber} 274 return file, file, nil 275 } 276 277 func (d Dir) Flush(ctx context.Context, req *fuselib.FlushRequest) error { 278 enterGate() 279 defer leaveGate() 280 281 err := d.volumeHandle.Flush(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber) 282 if err != nil { 283 err = newFuseError(err) 284 } 285 return err 286 } 287 288 func (d Dir) Fsync(ctx context.Context, req *fuselib.FsyncRequest) error { 289 enterGate() 290 defer leaveGate() 291 292 err := d.volumeHandle.Flush(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, 293 d.inodeNumber) 294 if err != nil { 295 err = newFuseError(err) 296 } 297 return err 298 } 299 300 func (d Dir) Mkdir(ctx context.Context, req *fuselib.MkdirRequest) (fusefslib.Node, error) { 301 enterGate() 302 defer leaveGate() 303 304 trimmedMode := inode.InodeMode(req.Mode) & inode.PosixModePerm 305 newDirInodeNumber, err := d.volumeHandle.Mkdir(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.Name, trimmedMode) 306 if err != nil { 307 err = newFuseError(err) 308 return nil, err 309 } 310 return Dir{volumeHandle: d.volumeHandle, inodeNumber: newDirInodeNumber}, nil 311 } 312 313 func (d Dir) Rename(ctx context.Context, req *fuselib.RenameRequest, newDir fusefslib.Node) error { 314 enterGate() 315 defer leaveGate() 316 317 dstDir, ok := newDir.(Dir) 318 if !ok { 319 return fuselib.EIO 320 } 321 err := d.volumeHandle.Rename(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.OldName, dstDir.inodeNumber, req.NewName) 322 if err != nil { 323 err = newFuseError(err) 324 } 325 return err 326 } 327 328 func (d Dir) Symlink(ctx context.Context, req *fuselib.SymlinkRequest) (fusefslib.Node, error) { 329 enterGate() 330 defer leaveGate() 331 332 symlinkInodeNumber, err := d.volumeHandle.Symlink(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.NewName, req.Target) 333 if err != nil { 334 err = newFuseError(err) 335 return nil, err 336 } 337 338 return Symlink{volumeHandle: d.volumeHandle, inodeNumber: symlinkInodeNumber}, nil 339 } 340 341 func (d Dir) Link(ctx context.Context, req *fuselib.LinkRequest, old fusefslib.Node) (fusefslib.Node, error) { 342 enterGate() 343 defer leaveGate() 344 345 oldFile, ok := old.(File) 346 if !ok { 347 err := fmt.Errorf("old.(File) failed") 348 return nil, err 349 } 350 351 err := d.volumeHandle.Link(inode.InodeUserID(req.Header.Uid), inode.InodeGroupID(req.Header.Gid), nil, d.inodeNumber, req.NewName, oldFile.inodeNumber) 352 if err != nil { 353 err = newFuseError(err) 354 } 355 356 return old, err 357 }