pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Patch to add NFSv4.2 SEEK support, 2025-01-29
Posted by Anonymous on Wed 29th Jan 2025 16:42
raw | new post

  1. From 6dcd3f3e6b9186c53fa6488b3d88a69341cad000 Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Wed, 29 Jan 2025 17:36:37 +0100
  4. Subject: [PATCH] RFE: Implement NFSv4.2 SEEK support (seek for data/hole
  5.  blocks)
  6.  
  7. Implement NFSv4.2 SEEK support, e.g. seek for data/hole blocks in
  8. sparse files.
  9.  
  10. Reported-by: Lionel Cons <lionelcons1972@gmail.com>
  11. Reported-by: Cedric Blancher <cedric.blancher@gmail.com>
  12. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  13. ---
  14. daemon/daemon_debug.c | 161 ++++++++++++++++++++++++++++++++++++++++++
  15.  daemon/daemon_debug.h |   2 +
  16.  daemon/nfs41_ops.h    |  29 ++++++++
  17.  daemon/nfs41_xdr.c    |   2 +-
  18.  daemon/nfs41_xdr.h    |   2 +
  19.  daemon/nfs42_ops.c    |  50 +++++++++++++
  20.  daemon/nfs42_xdr.c    |  58 +++++++++++++++
  21.  daemon/open.c         |   8 +++
  22.  daemon/recovery.c     |   3 +
  23.  9 files changed, 314 insertions(+), 1 deletion(-)
  24.  
  25. diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
  26. index 50a915e..16efbd9 100644
  27. --- a/daemon/daemon_debug.c
  28. +++ b/daemon/daemon_debug.c
  29. @@ -1115,6 +1115,167 @@ void print_nfs41_file_info(
  30.      dprintf_out("%s={ %s }\n", label, buf);
  31.  }
  32.  
  33. +void debug_list_sparsefile_holes(nfs41_open_state *state)
  34. +{
  35. +    int seek_status;
  36. +    bool_t seek_sr_eof = FALSE;
  37. +    uint64_t seek_sr_offset = 0ULL;
  38. +    uint64_t next_offset = 0ULL;
  39. +    stateid_arg stateid;
  40. +    int seek_cycle;
  41. +    bool is_sparse_file = false;
  42. +    bool file_has_data_blocks = false;
  43. +    uint64_t offset_of_first_data = ~0ULL;
  44. +    uint64_t offset_of_first_hole = ~0ULL;
  45. +
  46. +    dprintf_out(
  47. +        "--> debug_list_sparsefile_holes(state->path.path='%s')\n",
  48. +        state->path.path);
  49. +
  50. +    nfs41_open_stateid_arg(state, &stateid);
  51. +
  52. +    seek_status = nfs42_seek(state->session,
  53. +        &state->file,
  54. +        &stateid,
  55. +        0,
  56. +        NFS4_CONTENT_HOLE,
  57. +        &seek_sr_eof,
  58. +        &seek_sr_offset);
  59. +    if (seek_status) {
  60. +        dprintf_out("initial SEEK_HOLE failed "
  61. +        "OP_SEEK(sa_offset=%llu,sa_what=SEEK_HOLE) "
  62. +        "failed with %d(='%s')\n",
  63. +        0,
  64. +        seek_status,
  65. +        nfs_error_string(seek_status));
  66. +        goto out;
  67. +    }
  68. +
  69. +    offset_of_first_hole = seek_sr_offset;
  70. +
  71. +    /* Not a virtual hole at the end ? Then this is a sparse file */
  72. +    if (seek_sr_eof == FALSE) {
  73. +        is_sparse_file = true;
  74. +    }
  75. +
  76. +    seek_status = nfs42_seek(state->session,
  77. +        &state->file,
  78. +        &stateid,
  79. +        0,
  80. +        NFS4_CONTENT_DATA,
  81. +        &seek_sr_eof,
  82. +        &seek_sr_offset);
  83. +    if (seek_status && (seek_status != NFS4ERR_NXIO)) {
  84. +        dprintf_out("initial SEEL_DATA failed "
  85. +        "OP_SEEK(sa_offset=%llu,sa_what=SEEK_DATA) "
  86. +        "failed with %d(='%s')\n",
  87. +        0,
  88. +        seek_status,
  89. +        nfs_error_string(seek_status));
  90. +        goto out;
  91. +    }
  92. +
  93. +
  94. +    if (seek_status == NFS4ERR_NXIO) {
  95. +        file_has_data_blocks = false;
  96. +        offset_of_first_data = ~0ULL;
  97. +    }
  98. +    else {
  99. +        file_has_data_blocks = true;
  100. +        offset_of_first_data = seek_sr_offset;
  101. +    }
  102. +
  103. +    dprintf_out("INFO: "
  104. +        "is_sparse_file=%d "
  105. +        "file_has_data_blocks=%d, "
  106. +        "offset_of_first_hole=%llu, "
  107. +        "offset_of_first_data=%llu\n",
  108. +        (int)is_sparse_file, (int)file_has_data_blocks,
  109. +        (long long)offset_of_first_hole,
  110. +        (long long)offset_of_first_data);
  111. +
  112. +    data_content4 cycle_what;
  113. +
  114. +    /* Does the file start with data or a hole ? */
  115. +    if (file_has_data_blocks &&
  116. +        (offset_of_first_data < offset_of_first_hole)) {
  117. +        cycle_what = NFS4_CONTENT_DATA;
  118. +    }
  119. +    else {
  120. +        cycle_what = NFS4_CONTENT_HOLE;
  121. +    }
  122. +
  123. +    /*
  124. +     * Limit to 100 cycles to avoid locking-up the client if
  125. +     * something unexpected happen
  126. +     */
  127. +    for (seek_cycle = 0 ; seek_cycle < 100 ; seek_cycle++) {
  128. +        if ((file_has_data_blocks == false) &&
  129. +            (cycle_what == NFS4_CONTENT_DATA)) {
  130. +            dprintf_out("cycle=%d: "
  131. +                "no data blocks, skipping SEEK_DATA, assuming EOF.\n",
  132. +                seek_cycle);
  133. +            break;
  134. +        }
  135. +
  136. +        seek_status = nfs42_seek(state->session,
  137. +            &state->file,
  138. +            &stateid,
  139. +            next_offset,
  140. +            cycle_what,
  141. +            &seek_sr_eof,
  142. +            &seek_sr_offset);
  143. +
  144. +        if (seek_status) {
  145. +            dprintf_out("cycle=%d: "
  146. +                "OP_SEEK(sa_offset=%llu,sa_what='%s') "
  147. +                "failed with %d(='%s')\n",
  148. +                seek_cycle,
  149. +                next_offset,
  150. +                (cycle_what==NFS4_CONTENT_DATA?"SEEK_DATA":"SEEK_HOLE"),
  151. +                seek_status,
  152. +                nfs_error_string(seek_status));
  153. +            break;
  154. +        }
  155. +
  156. +        dprintf_out("cycle=%d: "
  157. +            "OP_SEEK(sa_offset=%llu,sa_what='%s') SUCCESS: "
  158. +            "sr_eof=%d, sr_offset=%lld\n",
  159. +            seek_cycle,
  160. +            next_offset,
  161. +            (cycle_what==NFS4_CONTENT_DATA?"SEEK_DATA":"SEEK_HOLE"),
  162. +            (int)seek_sr_eof,
  163. +            (long long)seek_sr_offset);
  164. +
  165. +        if (seek_sr_eof)
  166. +            break;
  167. +
  168. +        next_offset = seek_sr_offset;
  169. +
  170. +        /*
  171. +         * Note this assumes that "hole" and "data" are alternating,
  172. +         * and we never get a { ..., hole, hole, ... } or
  173. +         * { ... data, data ... }. The original |lseek()|
  174. +         * |SEEK_HOLE|/|SEEK_DATA| API would allow this, but it is
  175. +         * not clear whether the POSIX standard would allow this.
  176. +         *
  177. +         * Technically it would be usefull in case of pNFS, where one
  178. +         * file can be split over multiple data servers, and SEEK_DATA
  179. +         * would stop at the boundary of data_server_1, followed by
  180. +         * another SEEK_DATA which starts at the next data server.
  181. +         */
  182. +        if (cycle_what == NFS4_CONTENT_DATA) {
  183. +            cycle_what = NFS4_CONTENT_HOLE;
  184. +        }
  185. +        else {
  186. +            cycle_what = NFS4_CONTENT_DATA;
  187. +        }
  188. +    }
  189. +
  190. +out:
  191. +    dprintf_out("<-- debug_list_sparsefile_holes()\n");
  192. +}
  193. +
  194.  #define NUM_RECENTLY_DELETED 128
  195.  static struct
  196.  {
  197. diff --git a/daemon/daemon_debug.h b/daemon/daemon_debug.h
  198. index 969bf80..2d4cc93 100644
  199. --- a/daemon/daemon_debug.h
  200. +++ b/daemon/daemon_debug.h
  201. @@ -153,6 +153,8 @@ void open_log_files();
  202.  void close_log_files();
  203.  const char* secflavorop2name(DWORD sec_flavor);
  204.  void print_nfs41_file_info(const char *label, const void *vinfo);
  205. +typedef struct __nfs41_open_state nfs41_open_state;
  206. +void debug_list_sparsefile_holes(nfs41_open_state *state);
  207.  
  208.  /* pnfs_debug.c */
  209.  enum pnfs_status;
  210. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  211. index dd9330c..e687391 100644
  212. --- a/daemon/nfs41_ops.h
  213. +++ b/daemon/nfs41_ops.h
  214. @@ -788,6 +788,26 @@ typedef struct __nfs42_read_plus_res {
  215.      nfs42_read_plus_res_ok  resok4;
  216.  } nfs42_read_plus_res;
  217.  
  218. +#if 1
  219. +/* OP_SEEK */
  220. +typedef struct __nfs42_seek_args {
  221. +    stateid_arg     *stateid; /* -> nfs41_op_open_res_ok.stateid */
  222. +    uint64_t        offset;
  223. +    data_content4   what;
  224. +} nfs42_seek_args;
  225. +
  226. +typedef struct __nfs42_seek_res_ok {
  227. +    bool_t      eof;
  228. +    uint64_t    offset;
  229. +} nfs42_seek_res_ok;
  230. +
  231. +typedef struct __nfs42_seek_res {
  232. +    uint32_t            status;
  233. +    /* case NFS4_OK: */
  234. +    nfs42_seek_res_ok   resok4;
  235. +} nfs42_seek_res;
  236. +#endif
  237. +
  238.  /* OP_READDIR */
  239.  typedef struct __nfs41_readdir_args {
  240.      nfs41_readdir_cookie    cookie;
  241. @@ -1183,6 +1203,15 @@ int nfs42_read_plus(
  242.      OUT uint32_t *data_len_out,
  243.      OUT bool_t *eof_out);
  244.  
  245. +int nfs42_seek(
  246. +    IN nfs41_session *session,
  247. +    IN nfs41_path_fh *file,
  248. +    IN stateid_arg *stateid,
  249. +    IN uint64_t offset,
  250. +    IN data_content4 what,
  251. +    OUT bool_t *eof_out,
  252. +    OUT uint64_t *offset_out);
  253. +
  254.  int nfs41_commit(
  255.      IN nfs41_session *session,
  256.      IN nfs41_path_fh *file,
  257. diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
  258. index ecce707..afc4fbb 100644
  259. --- a/daemon/nfs41_xdr.c
  260. +++ b/daemon/nfs41_xdr.c
  261. @@ -3664,7 +3664,7 @@ static const op_table_entry g_op_table[] = {
  262.      { NULL, NULL }, /* OP_OFFLOAD_CANCEL = 66, */
  263.      { NULL, NULL }, /* OP_OFFLOAD_STATUS = 67, */
  264.      { encode_op_read_plus, decode_op_read_plus }, /* OP_READ_PLUS = 68, */
  265. -    { NULL, NULL }, /* OP_SEEK = 69, */
  266. +    { encode_op_seek, decode_op_seek }, /* OP_SEEK = 69, */
  267.      { NULL, NULL }, /* OP_WRITE_SAME = 70, */
  268.      { NULL, NULL }, /* OP_CLONE = 71, */
  269.  
  270. diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
  271. index 499fe86..d6f217f 100644
  272. --- a/daemon/nfs41_xdr.h
  273. +++ b/daemon/nfs41_xdr.h
  274. @@ -36,5 +36,7 @@ bool_t xdr_stateid4(XDR *xdr, stateid4 *si);
  275.  /* NFSv4.2 ops */
  276.  bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
  277.  bool_t decode_op_read_plus(XDR *xdr, nfs_resop4 *resop);
  278. +bool_t encode_op_seek(XDR *xdr, nfs_argop4 *argop);
  279. +bool_t decode_op_seek(XDR *xdr, nfs_resop4 *resop);
  280.  
  281.  #endif /* !__NFS41_NFS_XDR_H__ */
  282. diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
  283. index 287fd2e..39e946d 100644
  284. --- a/daemon/nfs42_ops.c
  285. +++ b/daemon/nfs42_ops.c
  286. @@ -94,3 +94,53 @@ int nfs42_read_plus(
  287.  out:
  288.      return status;
  289.  }
  290. +
  291. +int nfs42_seek(
  292. +    IN nfs41_session *session,
  293. +    IN nfs41_path_fh *file,
  294. +    IN stateid_arg *stateid,
  295. +    IN uint64_t offset,
  296. +    IN data_content4 what,
  297. +    OUT bool_t *eof_out,
  298. +    OUT uint64_t *offset_out)
  299. +{
  300. +    int status;
  301. +    nfs41_compound compound;
  302. +    nfs_argop4 argops[4];
  303. +    nfs_resop4 resops[4];
  304. +    nfs41_sequence_args sequence_args;
  305. +    nfs41_sequence_res sequence_res;
  306. +    nfs41_putfh_args putfh_args;
  307. +    nfs41_putfh_res putfh_res;
  308. +    nfs42_seek_args seek_args;
  309. +    nfs42_seek_res  seek_res;
  310. +
  311. +    compound_init(&compound, session->client->root->nfsminorvers,
  312. +        argops, resops,
  313. +        "seek");
  314. +
  315. +    compound_add_op(&compound, OP_SEQUENCE,
  316. +        &sequence_args, &sequence_res);
  317. +    nfs41_session_sequence(&sequence_args, session, 0);
  318. +
  319. +    compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
  320. +    putfh_args.file = file;
  321. +    putfh_args.in_recovery = 0;
  322. +
  323. +    compound_add_op(&compound, OP_SEEK, &seek_args, &seek_res);
  324. +    seek_args.stateid = stateid;
  325. +    seek_args.offset = offset;
  326. +    seek_args.what = what;
  327. +
  328. +    status = compound_encode_send_decode(session, &compound, TRUE);
  329. +    if (status)
  330. +        goto out;
  331. +
  332. +    if (compound_error(status = compound.res.status))
  333. +        goto out;
  334. +
  335. +    *eof_out = seek_res.resok4.eof;
  336. +    *offset_out = seek_res.resok4.offset;
  337. +out:
  338. +    return status;
  339. +}
  340. diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
  341. index a5d0d1c..9120461 100644
  342. --- a/daemon/nfs42_xdr.c
  343. +++ b/daemon/nfs42_xdr.c
  344. @@ -189,3 +189,61 @@ bool_t decode_op_read_plus(
  345.  
  346.      return TRUE;
  347.  }
  348. +
  349. +/*
  350. + * OP_SEEK
  351. + */
  352. +bool_t encode_op_seek(
  353. +    XDR *xdr,
  354. +    nfs_argop4 *argop)
  355. +{
  356. +    nfs42_seek_args *args = (nfs42_seek_args *)argop->arg;
  357. +
  358. +    if (unexpected_op(argop->op, OP_SEEK))
  359. +        return FALSE;
  360. +
  361. +    if (!xdr_stateid4(xdr, &args->stateid->stateid))
  362. +        return FALSE;
  363. +
  364. +    if (!xdr_u_hyper(xdr, &args->offset))
  365. +        return FALSE;
  366. +
  367. +    uint32_t args_what = args->what;
  368. +    EASSERT((args_what == NFS4_CONTENT_DATA) ||
  369. +        (args_what == NFS4_CONTENT_HOLE));
  370. +    return xdr_u_int32_t(xdr, &args_what);
  371. +}
  372. +
  373. +static bool_t decode_seek_res_ok(
  374. +    XDR *xdr,
  375. +    nfs42_seek_res_ok *res)
  376. +{
  377. +    if (!xdr_bool(xdr, &res->eof)) {
  378. +        DPRINTF(0, ("decode eof failed\n"));
  379. +        return FALSE;
  380. +    }
  381. +
  382. +    if (!xdr_u_hyper(xdr, &res->offset)) {
  383. +        return FALSE;
  384. +    }
  385. +
  386. +    return TRUE;
  387. +}
  388. +
  389. +bool_t decode_op_seek(
  390. +    XDR *xdr,
  391. +    nfs_resop4 *resop)
  392. +{
  393. +    nfs42_seek_res *res = (nfs42_seek_res *)resop->res;
  394. +
  395. +    if (unexpected_op(resop->op, OP_SEEK))
  396. +        return FALSE;
  397. +
  398. +    if (!xdr_u_int32_t(xdr, &res->status))
  399. +        return FALSE;
  400. +
  401. +    if (res->status == NFS4_OK)
  402. +        return decode_seek_res_ok(xdr, &res->resok4);
  403. +
  404. +    return TRUE;
  405. +}
  406. diff --git a/daemon/open.c b/daemon/open.c
  407. index 58e1348..26835dc 100644
  408. --- a/daemon/open.c
  409. +++ b/daemon/open.c
  410. @@ -1078,6 +1078,14 @@ create_chgrp_out:
  411.          }
  412.      }
  413.  
  414. +#ifdef DEBUG_OPEN_SPARSE_FILES
  415. +    if ((status == 0) &&
  416. +        (info.type == NF4REG) &&
  417. +        (state->session->client->root->nfsminorvers >= 2)) {
  418. +        debug_list_sparsefile_holes(state);
  419. +    }
  420. +#endif /* DEBUG_OPEN_SPARSE_FILES */
  421. +
  422.      upcall->state_ref = state;
  423.      nfs41_open_state_ref(upcall->state_ref);
  424.  out:
  425. diff --git a/daemon/recovery.c b/daemon/recovery.c
  426. index da94a7d..42ca27b 100644
  427. --- a/daemon/recovery.c
  428. +++ b/daemon/recovery.c
  429. @@ -809,6 +809,9 @@ bool_t nfs41_recover_stateid(
  430.      } else if (argop->op == OP_READ_PLUS) {
  431.          nfs42_read_plus_args *read_plus = (nfs42_read_plus_args *)argop->arg;
  432.          stateid = read_plus->stateid;
  433. +    } else if (argop->op == OP_SEEK) {
  434. +        nfs42_seek_args *seek = (nfs42_seek_args *)argop->arg;
  435. +        stateid = seek->stateid;
  436.      } else if (argop->op == OP_WRITE) {
  437.          nfs41_write_args *write = (nfs41_write_args*)argop->arg;
  438.          stateid = write->stateid;
  439. --
  440. 2.45.1

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at