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  }