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