Lseek
From Linux NFS
SEEK is an operation that allows a client to determine the location of the next data_content4 in a file. It allows an implementation of the emerging extension to lseek(2) to allow clients to determine SEEK_HOLE and SEEK_DATA.
Contents |
Data type reference
typedef uint64_t length4 typedef uint64_t offset4 enum data_content4 { NFS4_CONTENT_DATA = 0, NFS4_CONTENT_APP_DATA_HOLE = 1, NFS4_CONTENT_HOLE = 2, }; struct data_info4 { offset4 di_offset; length4 di_length; bool di_allocated; };
Argument
struct SEEK4args { /* CURRENT_FH: file */ stateid4 sa_stateid; offset4 sa_offset; data_content4 sa_what; };
Result
union seek_content switch (data_content4 content) { case NFS4_CONTENT_DATA: data_info4 sc_data; case NFS4_CONTENT_APP_DATA_HOLE: app_data_hole4 sc_adh; case NFS4_CONTENT_HOLE: data_info4 sc_hole; default: void; }; struct seek_res4 { bool sr_eof; seek_content sr_contents; }; union SEEK4res switch (nfsstat4 status) { case NFS4_OK: seek_res4 resok4; default: void; };
Client
- Continue to use nfs_file_llseek() in fs/nfs/file.c for NFS v2, v3, v4.0 and v4.1.
- Need to optimize for SEEK_SET, SEEK_CUR and SEEK_END cases (fall back to the VFS). I think this should be enough:
case SEEK_HOLE: case SEEK_DATA: return nfs42_file_llseek(); break; default: return nfs_file_llseek();
- Use the compound:
SEQUENCE PUTFH SEEK
- Flush the file to disk (nfs_wb_all) before attempting to seek that way the server knows where to find data.
- Send either NFS4_CONTENT_DATA xor NFS4_CONTENT_HOLE but not the NFS4_CONTENT_APP_DATA_HOLE option.
- Decode the data_info4 to find the di_offset and call vfs_setpos() with that value.
- Return -ENXIO if the server has sr_eof set, indicating an out-of-bounds seek.
Server
- Need to call preprocess_stateid_op() for both RD_STATE and WR_STATE. Is there a better way to do this?
- If sa_what == NFS4_CONTENT_DATA:
- di_offset = vfs_llseek(sa_offset, SEEK_DATA)
- di_length = vfs_llseek(di_offset, SEEK_HOLE) - di_offset
- If sa_what == NFS4_CONTENT_HOLE:
- di_offset = vfs_llseek(sa_offset, SEEK_HOLE)
- di_length = vfs_llseek(di_offset, SEEK_DATA) - di_offset
- Set sr_eof to true if di_offset == -ENXIO is returned and zero out the data_info4 fields in the result.
- I don't know how to find the value of di_allocated.
Testing
xfs tests #285 and #286 both test allocating files and punching holes through use of the lseek() function. They can be used for testing SEEK_HOLE and SEEK_DATA implementation.
Status
- Both client and server code has been written.
- Tests 7, 8, and 9 of xfstests generic/258 use fallocate() to punch holes and are therefore not running yet.
- Test 11 and 12 of generic/258 test huge files and currently have failing sub-tests, but the error is being generated by the vfs on the server.