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  }