github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fuse/file.go (about)

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