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