- From 6dcd3f3e6b9186c53fa6488b3d88a69341cad000 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Wed, 29 Jan 2025 17:36:37 +0100
- Subject: [PATCH] RFE: Implement NFSv4.2 SEEK support (seek for data/hole
- blocks)
- Implement NFSv4.2 SEEK support, e.g. seek for data/hole blocks in
- sparse files.
- Reported-by: Lionel Cons <lionelcons1972@gmail.com>
- Reported-by: Cedric Blancher <cedric.blancher@gmail.com>
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/daemon_debug.c | 161 ++++++++++++++++++++++++++++++++++++++++++
- daemon/daemon_debug.h | 2 +
- daemon/nfs41_ops.h | 29 ++++++++
- daemon/nfs41_xdr.c | 2 +-
- daemon/nfs41_xdr.h | 2 +
- daemon/nfs42_ops.c | 50 +++++++++++++
- daemon/nfs42_xdr.c | 58 +++++++++++++++
- daemon/open.c | 8 +++
- daemon/recovery.c | 3 +
- 9 files changed, 314 insertions(+), 1 deletion(-)
- diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
- index 50a915e..16efbd9 100644
- --- a/daemon/daemon_debug.c
- +++ b/daemon/daemon_debug.c
- @@ -1115,6 +1115,167 @@ void print_nfs41_file_info(
- dprintf_out("%s={ %s }\n", label, buf);
- }
- +void debug_list_sparsefile_holes(nfs41_open_state *state)
- +{
- + int seek_status;
- + bool_t seek_sr_eof = FALSE;
- + uint64_t seek_sr_offset = 0ULL;
- + uint64_t next_offset = 0ULL;
- + stateid_arg stateid;
- + int seek_cycle;
- + bool is_sparse_file = false;
- + bool file_has_data_blocks = false;
- + uint64_t offset_of_first_data = ~0ULL;
- + uint64_t offset_of_first_hole = ~0ULL;
- +
- + dprintf_out(
- + "--> debug_list_sparsefile_holes(state->path.path='%s')\n",
- + state->path.path);
- +
- + nfs41_open_stateid_arg(state, &stateid);
- +
- + seek_status = nfs42_seek(state->session,
- + &state->file,
- + &stateid,
- + 0,
- + NFS4_CONTENT_HOLE,
- + &seek_sr_eof,
- + &seek_sr_offset);
- + if (seek_status) {
- + dprintf_out("initial SEEK_HOLE failed "
- + "OP_SEEK(sa_offset=%llu,sa_what=SEEK_HOLE) "
- + "failed with %d(='%s')\n",
- + 0,
- + seek_status,
- + nfs_error_string(seek_status));
- + goto out;
- + }
- +
- + offset_of_first_hole = seek_sr_offset;
- +
- + /* Not a virtual hole at the end ? Then this is a sparse file */
- + if (seek_sr_eof == FALSE) {
- + is_sparse_file = true;
- + }
- +
- + seek_status = nfs42_seek(state->session,
- + &state->file,
- + &stateid,
- + 0,
- + NFS4_CONTENT_DATA,
- + &seek_sr_eof,
- + &seek_sr_offset);
- + if (seek_status && (seek_status != NFS4ERR_NXIO)) {
- + dprintf_out("initial SEEL_DATA failed "
- + "OP_SEEK(sa_offset=%llu,sa_what=SEEK_DATA) "
- + "failed with %d(='%s')\n",
- + 0,
- + seek_status,
- + nfs_error_string(seek_status));
- + goto out;
- + }
- +
- +
- + if (seek_status == NFS4ERR_NXIO) {
- + file_has_data_blocks = false;
- + offset_of_first_data = ~0ULL;
- + }
- + else {
- + file_has_data_blocks = true;
- + offset_of_first_data = seek_sr_offset;
- + }
- +
- + dprintf_out("INFO: "
- + "is_sparse_file=%d "
- + "file_has_data_blocks=%d, "
- + "offset_of_first_hole=%llu, "
- + "offset_of_first_data=%llu\n",
- + (int)is_sparse_file, (int)file_has_data_blocks,
- + (long long)offset_of_first_hole,
- + (long long)offset_of_first_data);
- +
- + data_content4 cycle_what;
- +
- + /* Does the file start with data or a hole ? */
- + if (file_has_data_blocks &&
- + (offset_of_first_data < offset_of_first_hole)) {
- + cycle_what = NFS4_CONTENT_DATA;
- + }
- + else {
- + cycle_what = NFS4_CONTENT_HOLE;
- + }
- +
- + /*
- + * Limit to 100 cycles to avoid locking-up the client if
- + * something unexpected happen
- + */
- + for (seek_cycle = 0 ; seek_cycle < 100 ; seek_cycle++) {
- + if ((file_has_data_blocks == false) &&
- + (cycle_what == NFS4_CONTENT_DATA)) {
- + dprintf_out("cycle=%d: "
- + "no data blocks, skipping SEEK_DATA, assuming EOF.\n",
- + seek_cycle);
- + break;
- + }
- +
- + seek_status = nfs42_seek(state->session,
- + &state->file,
- + &stateid,
- + next_offset,
- + cycle_what,
- + &seek_sr_eof,
- + &seek_sr_offset);
- +
- + if (seek_status) {
- + dprintf_out("cycle=%d: "
- + "OP_SEEK(sa_offset=%llu,sa_what='%s') "
- + "failed with %d(='%s')\n",
- + seek_cycle,
- + next_offset,
- + (cycle_what==NFS4_CONTENT_DATA?"SEEK_DATA":"SEEK_HOLE"),
- + seek_status,
- + nfs_error_string(seek_status));
- + break;
- + }
- +
- + dprintf_out("cycle=%d: "
- + "OP_SEEK(sa_offset=%llu,sa_what='%s') SUCCESS: "
- + "sr_eof=%d, sr_offset=%lld\n",
- + seek_cycle,
- + next_offset,
- + (cycle_what==NFS4_CONTENT_DATA?"SEEK_DATA":"SEEK_HOLE"),
- + (int)seek_sr_eof,
- + (long long)seek_sr_offset);
- +
- + if (seek_sr_eof)
- + break;
- +
- + next_offset = seek_sr_offset;
- +
- + /*
- + * Note this assumes that "hole" and "data" are alternating,
- + * and we never get a { ..., hole, hole, ... } or
- + * { ... data, data ... }. The original |lseek()|
- + * |SEEK_HOLE|/|SEEK_DATA| API would allow this, but it is
- + * not clear whether the POSIX standard would allow this.
- + *
- + * Technically it would be usefull in case of pNFS, where one
- + * file can be split over multiple data servers, and SEEK_DATA
- + * would stop at the boundary of data_server_1, followed by
- + * another SEEK_DATA which starts at the next data server.
- + */
- + if (cycle_what == NFS4_CONTENT_DATA) {
- + cycle_what = NFS4_CONTENT_HOLE;
- + }
- + else {
- + cycle_what = NFS4_CONTENT_DATA;
- + }
- + }
- +
- +out:
- + dprintf_out("<-- debug_list_sparsefile_holes()\n");
- +}
- +
- #define NUM_RECENTLY_DELETED 128
- static struct
- {
- diff --git a/daemon/daemon_debug.h b/daemon/daemon_debug.h
- index 969bf80..2d4cc93 100644
- --- a/daemon/daemon_debug.h
- +++ b/daemon/daemon_debug.h
- @@ -153,6 +153,8 @@ void open_log_files();
- void close_log_files();
- const char* secflavorop2name(DWORD sec_flavor);
- void print_nfs41_file_info(const char *label, const void *vinfo);
- +typedef struct __nfs41_open_state nfs41_open_state;
- +void debug_list_sparsefile_holes(nfs41_open_state *state);
- /* pnfs_debug.c */
- enum pnfs_status;
- diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
- index dd9330c..e687391 100644
- --- a/daemon/nfs41_ops.h
- +++ b/daemon/nfs41_ops.h
- @@ -788,6 +788,26 @@ typedef struct __nfs42_read_plus_res {
- nfs42_read_plus_res_ok resok4;
- } nfs42_read_plus_res;
- +#if 1
- +/* OP_SEEK */
- +typedef struct __nfs42_seek_args {
- + stateid_arg *stateid; /* -> nfs41_op_open_res_ok.stateid */
- + uint64_t offset;
- + data_content4 what;
- +} nfs42_seek_args;
- +
- +typedef struct __nfs42_seek_res_ok {
- + bool_t eof;
- + uint64_t offset;
- +} nfs42_seek_res_ok;
- +
- +typedef struct __nfs42_seek_res {
- + uint32_t status;
- + /* case NFS4_OK: */
- + nfs42_seek_res_ok resok4;
- +} nfs42_seek_res;
- +#endif
- +
- /* OP_READDIR */
- typedef struct __nfs41_readdir_args {
- nfs41_readdir_cookie cookie;
- @@ -1183,6 +1203,15 @@ int nfs42_read_plus(
- OUT uint32_t *data_len_out,
- OUT bool_t *eof_out);
- +int nfs42_seek(
- + IN nfs41_session *session,
- + IN nfs41_path_fh *file,
- + IN stateid_arg *stateid,
- + IN uint64_t offset,
- + IN data_content4 what,
- + OUT bool_t *eof_out,
- + OUT uint64_t *offset_out);
- +
- int nfs41_commit(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
- index ecce707..afc4fbb 100644
- --- a/daemon/nfs41_xdr.c
- +++ b/daemon/nfs41_xdr.c
- @@ -3664,7 +3664,7 @@ static const op_table_entry g_op_table[] = {
- { NULL, NULL }, /* OP_OFFLOAD_CANCEL = 66, */
- { NULL, NULL }, /* OP_OFFLOAD_STATUS = 67, */
- { encode_op_read_plus, decode_op_read_plus }, /* OP_READ_PLUS = 68, */
- - { NULL, NULL }, /* OP_SEEK = 69, */
- + { encode_op_seek, decode_op_seek }, /* OP_SEEK = 69, */
- { NULL, NULL }, /* OP_WRITE_SAME = 70, */
- { NULL, NULL }, /* OP_CLONE = 71, */
- diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
- index 499fe86..d6f217f 100644
- --- a/daemon/nfs41_xdr.h
- +++ b/daemon/nfs41_xdr.h
- @@ -36,5 +36,7 @@ bool_t xdr_stateid4(XDR *xdr, stateid4 *si);
- /* NFSv4.2 ops */
- bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
- bool_t decode_op_read_plus(XDR *xdr, nfs_resop4 *resop);
- +bool_t encode_op_seek(XDR *xdr, nfs_argop4 *argop);
- +bool_t decode_op_seek(XDR *xdr, nfs_resop4 *resop);
- #endif /* !__NFS41_NFS_XDR_H__ */
- diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
- index 287fd2e..39e946d 100644
- --- a/daemon/nfs42_ops.c
- +++ b/daemon/nfs42_ops.c
- @@ -94,3 +94,53 @@ int nfs42_read_plus(
- out:
- return status;
- }
- +
- +int nfs42_seek(
- + IN nfs41_session *session,
- + IN nfs41_path_fh *file,
- + IN stateid_arg *stateid,
- + IN uint64_t offset,
- + IN data_content4 what,
- + OUT bool_t *eof_out,
- + OUT uint64_t *offset_out)
- +{
- + int status;
- + nfs41_compound compound;
- + nfs_argop4 argops[4];
- + nfs_resop4 resops[4];
- + nfs41_sequence_args sequence_args;
- + nfs41_sequence_res sequence_res;
- + nfs41_putfh_args putfh_args;
- + nfs41_putfh_res putfh_res;
- + nfs42_seek_args seek_args;
- + nfs42_seek_res seek_res;
- +
- + compound_init(&compound, session->client->root->nfsminorvers,
- + argops, resops,
- + "seek");
- +
- + compound_add_op(&compound, OP_SEQUENCE,
- + &sequence_args, &sequence_res);
- + nfs41_session_sequence(&sequence_args, session, 0);
- +
- + compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
- + putfh_args.file = file;
- + putfh_args.in_recovery = 0;
- +
- + compound_add_op(&compound, OP_SEEK, &seek_args, &seek_res);
- + seek_args.stateid = stateid;
- + seek_args.offset = offset;
- + seek_args.what = what;
- +
- + status = compound_encode_send_decode(session, &compound, TRUE);
- + if (status)
- + goto out;
- +
- + if (compound_error(status = compound.res.status))
- + goto out;
- +
- + *eof_out = seek_res.resok4.eof;
- + *offset_out = seek_res.resok4.offset;
- +out:
- + return status;
- +}
- diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
- index a5d0d1c..9120461 100644
- --- a/daemon/nfs42_xdr.c
- +++ b/daemon/nfs42_xdr.c
- @@ -189,3 +189,61 @@ bool_t decode_op_read_plus(
- return TRUE;
- }
- +
- +/*
- + * OP_SEEK
- + */
- +bool_t encode_op_seek(
- + XDR *xdr,
- + nfs_argop4 *argop)
- +{
- + nfs42_seek_args *args = (nfs42_seek_args *)argop->arg;
- +
- + if (unexpected_op(argop->op, OP_SEEK))
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &args->stateid->stateid))
- + return FALSE;
- +
- + if (!xdr_u_hyper(xdr, &args->offset))
- + return FALSE;
- +
- + uint32_t args_what = args->what;
- + EASSERT((args_what == NFS4_CONTENT_DATA) ||
- + (args_what == NFS4_CONTENT_HOLE));
- + return xdr_u_int32_t(xdr, &args_what);
- +}
- +
- +static bool_t decode_seek_res_ok(
- + XDR *xdr,
- + nfs42_seek_res_ok *res)
- +{
- + if (!xdr_bool(xdr, &res->eof)) {
- + DPRINTF(0, ("decode eof failed\n"));
- + return FALSE;
- + }
- +
- + if (!xdr_u_hyper(xdr, &res->offset)) {
- + return FALSE;
- + }
- +
- + return TRUE;
- +}
- +
- +bool_t decode_op_seek(
- + XDR *xdr,
- + nfs_resop4 *resop)
- +{
- + nfs42_seek_res *res = (nfs42_seek_res *)resop->res;
- +
- + if (unexpected_op(resop->op, OP_SEEK))
- + return FALSE;
- +
- + if (!xdr_u_int32_t(xdr, &res->status))
- + return FALSE;
- +
- + if (res->status == NFS4_OK)
- + return decode_seek_res_ok(xdr, &res->resok4);
- +
- + return TRUE;
- +}
- diff --git a/daemon/open.c b/daemon/open.c
- index 58e1348..26835dc 100644
- --- a/daemon/open.c
- +++ b/daemon/open.c
- @@ -1078,6 +1078,14 @@ create_chgrp_out:
- }
- }
- +#ifdef DEBUG_OPEN_SPARSE_FILES
- + if ((status == 0) &&
- + (info.type == NF4REG) &&
- + (state->session->client->root->nfsminorvers >= 2)) {
- + debug_list_sparsefile_holes(state);
- + }
- +#endif /* DEBUG_OPEN_SPARSE_FILES */
- +
- upcall->state_ref = state;
- nfs41_open_state_ref(upcall->state_ref);
- out:
- diff --git a/daemon/recovery.c b/daemon/recovery.c
- index da94a7d..42ca27b 100644
- --- a/daemon/recovery.c
- +++ b/daemon/recovery.c
- @@ -809,6 +809,9 @@ bool_t nfs41_recover_stateid(
- } else if (argop->op == OP_READ_PLUS) {
- nfs42_read_plus_args *read_plus = (nfs42_read_plus_args *)argop->arg;
- stateid = read_plus->stateid;
- + } else if (argop->op == OP_SEEK) {
- + nfs42_seek_args *seek = (nfs42_seek_args *)argop->arg;
- + stateid = seek->stateid;
- } else if (argop->op == OP_WRITE) {
- nfs41_write_args *write = (nfs41_write_args*)argop->arg;
- stateid = write->stateid;
- --
- 2.45.1
msnfs41client: Patch to add NFSv4.2 SEEK support, 2025-01-29
Posted by Anonymous on Wed 29th Jan 2025 16:42
raw | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.