diff --git a/fuseops/ops.go b/fuseops/ops.go index d9bacc5..5df0c8f 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -239,6 +239,92 @@ type OpenDirOp struct { // Read entries from a directory previously opened with OpenDir. type ReadDirOp struct { + Header RequestHeader + + // The directory inode that we are reading, and the handle previously + // returned by OpenDir when opening that inode. + Inode InodeID + Handle HandleID + + // The offset within the directory at which to read. + // + // Warning: this field is not necessarily a count of bytes. Its legal values + // are defined by the results returned in ReadDirResponse. See the notes + // below and the notes on that struct. + // + // In the Linux kernel this ultimately comes from file::f_pos, which starts + // at zero and is set by llseek and by the final consumed result returned by + // each call to ReadDir: + // + // * (http://goo.gl/2nWJPL) iterate_dir, which is called by getdents(2) and + // readdir(2), sets dir_context::pos to file::f_pos before calling + // f_op->iterate, and then does the opposite assignment afterward. + // + // * (http://goo.gl/rTQVSL) fuse_readdir, which implements iterate for fuse + // directories, passes dir_context::pos as the offset to fuse_read_fill, + // which passes it on to user-space. fuse_readdir later calls + // parse_dirfile with the same context. + // + // * (http://goo.gl/vU5ukv) For each returned result (except perhaps the + // last, which may be truncated by the page boundary), parse_dirfile + // updates dir_context::pos with fuse_dirent::off. + // + // It is affected by the Posix directory stream interfaces in the following + // manner: + // + // * (http://goo.gl/fQhbyn, http://goo.gl/ns1kDF) opendir initially causes + // filepos to be set to zero. + // + // * (http://goo.gl/ezNKyR, http://goo.gl/xOmDv0) readdir allows the user + // to iterate through the directory one entry at a time. As each entry is + // consumed, its d_off field is stored in __dirstream::filepos. + // + // * (http://goo.gl/WEOXG8, http://goo.gl/rjSXl3) telldir allows the user + // to obtain the d_off field from the most recently returned entry. + // + // * (http://goo.gl/WG3nDZ, http://goo.gl/Lp0U6W) seekdir allows the user + // to seek backward to an offset previously returned by telldir. It + // stores the new offset in filepos, and calls llseek to update the + // kernel's struct file. + // + // * (http://goo.gl/gONQhz, http://goo.gl/VlrQkc) rewinddir allows the user + // to go back to the beginning of the directory, obtaining a fresh view. + // It updates filepos and calls llseek to update the kernel's struct + // file. + // + // Unfortunately, FUSE offers no way to intercept seeks + // (http://goo.gl/H6gEXa), so there is no way to cause seekdir or rewinddir + // to fail. Additionally, there is no way to distinguish an explicit + // rewinddir followed by readdir from the initial readdir, or a rewinddir + // from a seekdir to the value returned by telldir just after opendir. + // + // Luckily, Posix is vague about what the user will see if they seek + // backwards, and requires the user not to seek to an old offset after a + // rewind. The only requirement on freshness is that rewinddir results in + // something that looks like a newly-opened directory. So FUSE file systems + // may e.g. cache an entire fresh listing for each ReadDir with a zero + // offset, and return array offsets into that cached listing. + Offset DirOffset + + // The maximum number of bytes to return in ReadDirResponse.Data. A smaller + // number is acceptable. + Size int + + // Set by the file system: a buffer consisting of a sequence of FUSE + // directory entries in the format generated by fuse_add_direntry + // (http://goo.gl/qCcHCV), which is consumed by parse_dirfile + // (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to generate this data. + // + // The buffer must not exceed the length specified in ReadDirRequest.Size. It + // is okay for the final entry to be truncated; parse_dirfile copes with this + // by ignoring the partial record. + // + // Each entry returned exposes a directory offset to the user that may later + // show up in ReadDirRequest.Offset. See notes on that field for more + // information. + // + // An empty buffer indicates the end of the directory has been reached. + Data []byte } // Release a previously-minted directory handle. The kernel sends this when @@ -248,6 +334,12 @@ type ReadDirOp struct { // The kernel guarantees that the handle ID will not be used in further ops // sent to the file system (unless it is reissued by the file system). type ReleaseDirHandleOp struct { + Header RequestHeader + + // The handle ID to be released. The kernel guarantees that this ID will not + // be used in further calls to the file system (unless it is reissued by the + // file system). + Handle HandleID } //////////////////////////////////////////////////////////////////////// @@ -261,6 +353,22 @@ type ReleaseDirHandleOp struct { // process. On OS X it may not be sent for every open(2) // (cf.https://github.com/osxfuse/osxfuse/issues/199). type OpenFileOp struct { + Header RequestHeader + + // The ID of the inode to be opened. + Inode InodeID + + // Mode and options flags. + Flags bazilfuse.OpenFlags + + // An opaque ID that will be echoed in follow-up calls for this file using + // the same struct file in the kernel. In practice this usually means + // follow-up calls using the file descriptor returned by open(2). + // + // The handle may be supplied in future ops like ReadFileOp that contain a + // file handle. The file system must ensure this ID remains valid until a + // later call to ReleaseFileHandle. + Handle HandleID } // Read data from a file previously opened with CreateFile or OpenFile. @@ -269,6 +377,27 @@ type OpenFileOp struct { // some reads may be served by the page cache. See notes on WriteFileOp for // more. type ReadFileOp struct { + Header RequestHeader + + // The file inode that we are reading, and the handle previously returned by + // CreateFile or OpenFile when opening that inode. + Inode InodeID + Handle HandleID + + // The range of the file to read. + // + // The FUSE documentation requires that exactly the number of bytes be + // returned, except in the case of EOF or error (http://goo.gl/ZgfBkF). This + // appears to be because it uses file mmapping machinery + // (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand + // where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned + // by a previous call to LookUpInode, GetInodeAttributes, etc. + Offset int64 + Size int + + // Set by the file system: the data read. If this is less than the requested + // size, it indicates EOF. An error should not be returned in this case. + Data []byte } // Write data to a file previously opened with CreateFile or OpenFile. @@ -300,6 +429,42 @@ type ReadFileOp struct { // flush request. // type WriteFileOp struct { + Header RequestHeader + + // The file inode that we are modifying, and the handle previously returned + // by CreateFile or OpenFile when opening that inode. + Inode InodeID + Handle HandleID + + // The offset at which to write the data below. + // + // The man page for pwrite(2) implies that aside from changing the file + // handle's offset, using pwrite is equivalent to using lseek(2) and then + // write(2). The man page for lseek(2) says the following: + // + // "The lseek() function allows the file offset to be set beyond the end of + // the file (but this does not change the size of the file). If data is later + // written at this point, subsequent reads of the data in the gap (a "hole") + // return null bytes (aq\0aq) until data is actually written into the gap." + // + // It is therefore reasonable to assume that the kernel is looking for + // the following semantics: + // + // * If the offset is less than or equal to the current size, extend the + // file as necessary to fit any data that goes past the end of the file. + // + // * If the offset is greater than the current size, extend the file + // with null bytes until it is not, then do the above. + // + Offset int64 + + // The data to write. + // + // The FUSE documentation requires that exactly the number of bytes supplied + // be written, except on error (http://goo.gl/KUpwwn). This appears to be + // because it uses file mmapping machinery (http://goo.gl/SGxnaN) to write a + // page at a time. + Data []byte } // Synchronize the current contents of an open file to storage. @@ -384,198 +549,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type ReadDirRequest struct { - Header RequestHeader - - // The directory inode that we are reading, and the handle previously - // returned by OpenDir when opening that inode. - Inode InodeID - Handle HandleID - - // The offset within the directory at which to read. - // - // Warning: this field is not necessarily a count of bytes. Its legal values - // are defined by the results returned in ReadDirResponse. See the notes - // below and the notes on that struct. - // - // In the Linux kernel this ultimately comes from file::f_pos, which starts - // at zero and is set by llseek and by the final consumed result returned by - // each call to ReadDir: - // - // * (http://goo.gl/2nWJPL) iterate_dir, which is called by getdents(2) and - // readdir(2), sets dir_context::pos to file::f_pos before calling - // f_op->iterate, and then does the opposite assignment afterward. - // - // * (http://goo.gl/rTQVSL) fuse_readdir, which implements iterate for fuse - // directories, passes dir_context::pos as the offset to fuse_read_fill, - // which passes it on to user-space. fuse_readdir later calls - // parse_dirfile with the same context. - // - // * (http://goo.gl/vU5ukv) For each returned result (except perhaps the - // last, which may be truncated by the page boundary), parse_dirfile - // updates dir_context::pos with fuse_dirent::off. - // - // It is affected by the Posix directory stream interfaces in the following - // manner: - // - // * (http://goo.gl/fQhbyn, http://goo.gl/ns1kDF) opendir initially causes - // filepos to be set to zero. - // - // * (http://goo.gl/ezNKyR, http://goo.gl/xOmDv0) readdir allows the user - // to iterate through the directory one entry at a time. As each entry is - // consumed, its d_off field is stored in __dirstream::filepos. - // - // * (http://goo.gl/WEOXG8, http://goo.gl/rjSXl3) telldir allows the user - // to obtain the d_off field from the most recently returned entry. - // - // * (http://goo.gl/WG3nDZ, http://goo.gl/Lp0U6W) seekdir allows the user - // to seek backward to an offset previously returned by telldir. It - // stores the new offset in filepos, and calls llseek to update the - // kernel's struct file. - // - // * (http://goo.gl/gONQhz, http://goo.gl/VlrQkc) rewinddir allows the user - // to go back to the beginning of the directory, obtaining a fresh view. - // It updates filepos and calls llseek to update the kernel's struct - // file. - // - // Unfortunately, FUSE offers no way to intercept seeks - // (http://goo.gl/H6gEXa), so there is no way to cause seekdir or rewinddir - // to fail. Additionally, there is no way to distinguish an explicit - // rewinddir followed by readdir from the initial readdir, or a rewinddir - // from a seekdir to the value returned by telldir just after opendir. - // - // Luckily, Posix is vague about what the user will see if they seek - // backwards, and requires the user not to seek to an old offset after a - // rewind. The only requirement on freshness is that rewinddir results in - // something that looks like a newly-opened directory. So FUSE file systems - // may e.g. cache an entire fresh listing for each ReadDir with a zero - // offset, and return array offsets into that cached listing. - Offset DirOffset - - // The maximum number of bytes to return in ReadDirResponse.Data. A smaller - // number is acceptable. - Size int -} - -type ReadDirResponse struct { - // A buffer consisting of a sequence of FUSE directory entries in the format - // generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is consumed - // by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to - // generate this data. - // - // The buffer must not exceed the length specified in ReadDirRequest.Size. It - // is okay for the final entry to be truncated; parse_dirfile copes with this - // by ignoring the partial record. - // - // Each entry returned exposes a directory offset to the user that may later - // show up in ReadDirRequest.Offset. See notes on that field for more - // information. - // - // An empty buffer indicates the end of the directory has been reached. - Data []byte -} - -type ReleaseDirHandleRequest struct { - Header RequestHeader - - // The handle ID to be released. The kernel guarantees that this ID will not - // be used in further calls to the file system (unless it is reissued by the - // file system). - Handle HandleID -} - -type ReleaseDirHandleResponse struct { -} - -type OpenFileRequest struct { - Header RequestHeader - - // The ID of the inode to be opened. - Inode InodeID - - // Mode and options flags. - Flags bazilfuse.OpenFlags -} - -type OpenFileResponse struct { - // An opaque ID that will be echoed in follow-up calls for this file using - // the same struct file in the kernel. In practice this usually means - // follow-up calls using the file descriptor returned by open(2). - // - // The handle may be supplied in future ops like ReadFileOp that contain a - // file handle. The file system must ensure this ID remains valid until a - // later call to ReleaseFileHandle. - Handle HandleID -} - -type ReadFileRequest struct { - Header RequestHeader - - // The file inode that we are reading, and the handle previously returned by - // CreateFile or OpenFile when opening that inode. - Inode InodeID - Handle HandleID - - // The range of the file to read. - // - // The FUSE documentation requires that exactly the number of bytes be - // returned, except in the case of EOF or error (http://goo.gl/ZgfBkF). This - // appears to be because it uses file mmapping machinery - // (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand - // where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned - // by a previous call to LookUpInode, GetInodeAttributes, etc. - Offset int64 - Size int -} - -type ReadFileResponse struct { - // The data read. If this is less than the requested size, it indicates EOF. - // An error should not be returned in this case. - Data []byte -} - -type WriteFileRequest struct { - Header RequestHeader - - // The file inode that we are modifying, and the handle previously returned - // by CreateFile or OpenFile when opening that inode. - Inode InodeID - Handle HandleID - - // The offset at which to write the data below. - // - // The man page for pwrite(2) implies that aside from changing the file - // handle's offset, using pwrite is equivalent to using lseek(2) and then - // write(2). The man page for lseek(2) says the following: - // - // "The lseek() function allows the file offset to be set beyond the end of - // the file (but this does not change the size of the file). If data is later - // written at this point, subsequent reads of the data in the gap (a "hole") - // return null bytes (aq\0aq) until data is actually written into the gap." - // - // It is therefore reasonable to assume that the kernel is looking for - // the following semantics: - // - // * If the offset is less than or equal to the current size, extend the - // file as necessary to fit any data that goes past the end of the file. - // - // * If the offset is greater than the current size, extend the file - // with null bytes until it is not, then do the above. - // - Offset int64 - - // The data to write. - // - // The FUSE documentation requires that exactly the number of bytes supplied - // be written, except on error (http://goo.gl/KUpwwn). This appears to be - // because it uses file mmapping machinery (http://goo.gl/SGxnaN) to write a - // page at a time. - Data []byte -} - -type WriteFileResponse struct { -} - type SyncFileRequest struct { Header RequestHeader