{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/andrewkelley-me/2025-07-14-openzfs-bug-ported-to-zig/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "Someone on IRC shared this link with me: [An (almost) catastrophic OpenZFS bug and the humans that made it (and Rust is here too)](https://despairlabs.com/blog/posts/2025-07-10-an-openzfs-bug-and-the-humans-that-made-it/)\n\nThey wanted to know, would Zig catch this?\n\nI went through the trouble of porting the code snippet to Zig, so I thought I'd go the next step and turn it into a small blog post.\n\nOriginal snippet, in C:\n\n```\n/*\n * This code converts an asize into the largest psize that can safely be written\n * to an allocation of that size for this vdev.\n *\n * Note that this function will not take into account the effect of gang\n * headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n * the psize_to_asize function.\n */\nstatic uint64_t\nvdev_raidz_asize_to_psize(vdev_t *vd, uint64_t asize, uint64_t txg)\n{\n\tvdev_raidz_t *vdrz = vd->vdev_tsd;\n\tuint64_t psize;\n\tuint64_t ashift = vd->vdev_top->vdev_ashift;\n\tuint64_t cols = vdrz->vd_original_width;\n\tuint64_t nparity = vdrz->vd_nparity;\n\n\tcols = vdev_raidz_get_logical_width(vdrz, txg);\n\n\tASSERT0(asize % (1 << ashift));\n\n\tpsize = (asize >> ashift);\n\tpsize -= nparity * DIV_ROUND_UP(psize, cols);\n\tpsize <<= ashift;\n\n\treturn (asize);\n}\n```\n\nThe blog post author encourages us to try to [spot the fail](https://andrewkelley.me/post/spot-the-fail.html) before the answer is revealed.\n\nI couldn't do it before getting bored, so I ported the code to Zig instead:\n\n```\nconst std = @import(\"std\");\nconst divCeil = std.math.divCeil;\nconst assert = std.debug.assert;\nconst vdev_t = @import(\"the_rest_of_the_software.zig\").vdev_t;\n\n/// This code converts an asize into the largest psize that can safely be written\n/// to an allocation of that size for this vdev.\n///\n/// Note that this function will not take into account the effect of gang\n/// headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n/// the psize_to_asize function.\nfn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 {\n    const vdrz = vd.vdev_tsd;\n    const ashift = vd.vdev_top.vdev_ashift;\n    const cols = vdrz.vd_original_width;\n    const nparity = vdrz.vd_nparity;\n\n    const cols = vdrz.get_logical_width(txg);\n\n    assert(asize % (1 << ashift));\n\n    const asize_shifted = (asize >> ashift);\n    const parity_adjusted = asize_shifted - nparity * (divCeil(asize_shifted, cols) catch unreachable);\n    const psize = parity_adjusted << ashift;\n\n    return asize;\n}\n```\n\nLet's start with the code formatting tool alone, `zig fmt`:\n\n```\nandy@bark ~/tmp> zig fmt bug.zig --ast-check\nbug.zig:18:11: error: redeclaration of local constant 'cols'\n    const cols = vdrz.get_logical_width(txg);\n          ^~~~\nbug.zig:15:11: note: previous declaration here\n    const cols = vdrz.vd_original_width;\n          ^~~~\n```\n\nHmm, alright. That's definitely strange. The original blog post didn't notice that the initialization value of `cols` is dead code. That doesn't look like a logic error though. Let's fix it and try again:\n\n```\n@@ -12,7 +12,6 @@\n fn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 {\n     const vdrz = vd.vdev_tsd;\n     const ashift = vd.vdev_top.vdev_ashift;\n-    const cols = vdrz.vd_original_width;\n     const nparity = vdrz.vd_nparity;\n\n     const cols = vdrz.get_logical_width(txg);\n```\n\n```\nandy@bark ~/tmp> zig fmt bug.zig --ast-check\nbug.zig:23:11: error: unused local constant\n    const psize = parity_adjusted << ashift;\n          ^~~~~\n```\n\nBingo. We didn't even have to use the type checker.\n\nThanks for reading my blog post.\n",
  "authors": [],
  "captured_at": "2026-09-19T21:30:23.395188495+00:00",
  "category": "programming",
  "content_html": "<p>Someone on IRC shared this link with me: <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://despairlabs.com/blog/posts/2025-07-10-an-openzfs-bug-and-the-humans-that-made-it/\">An (almost) catastrophic OpenZFS bug and the humans that made it (and Rust is here too)</a></p>\n<p>They wanted to know, would Zig catch this?</p>\n<p>I went through the trouble of porting the code snippet to Zig, so I thought I'd go the next step and turn it into a small blog post.</p>\n<p>Original snippet, in C:</p>\n<pre><code><span class=\"code-snippet\">/*\n * This code converts an asize into the largest psize that can safely be written\n * to an allocation of that size for this vdev.\n *\n * Note that this function will not take into account the effect of gang\n * headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n * the psize_to_asize function.\n */\nstatic uint64_t\nvdev_raidz_asize_to_psize(vdev_t *vd, uint64_t asize, uint64_t txg)\n{\n\tvdev_raidz_t *vdrz = vd-&gt;vdev_tsd;\n\tuint64_t psize;\n\tuint64_t ashift = vd-&gt;vdev_top-&gt;vdev_ashift;\n\tuint64_t cols = vdrz-&gt;vd_original_width;\n\tuint64_t nparity = vdrz-&gt;vd_nparity;\n\n\tcols = vdev_raidz_get_logical_width(vdrz, txg);\n\n\tASSERT0(asize % (1 &lt;&lt; ashift));\n\n\tpsize = (asize &gt;&gt; ashift);\n\tpsize -= nparity * DIV_ROUND_UP(psize, cols);\n\tpsize &lt;&lt;= ashift;\n\n\treturn (asize);\n}\n</span></code></pre>\n<p>The blog post author encourages us to try to <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://andrewkelley.me/post/spot-the-fail.html\">spot the fail</a> before the answer is revealed.</p>\n<p>I couldn't do it before getting bored, so I ported the code to Zig instead:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"JavaScript\"><span class=\"syntax-source syntax-js\"><span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">std</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> @<span class=\"syntax-meta syntax-import syntax-js\"><span class=\"syntax-keyword syntax-control syntax-import-export syntax-js\">import</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-string syntax-quoted syntax-double syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-js\">&quot;</span>std<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-js\">&quot;</span></span><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n<span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">divCeil</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">std</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">math</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">divCeil</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n<span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-support syntax-module syntax-node syntax-js\">assert</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">std</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">debug</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">assert</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n<span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vdev_t</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> @<span class=\"syntax-meta syntax-import syntax-js\"><span class=\"syntax-keyword syntax-control syntax-import-export syntax-js\">import</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-string syntax-quoted syntax-double syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-js\">&quot;</span>the_rest_of_the_software.zig<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-js\">&quot;</span></span><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_t</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n<span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/ This code converts an asize into the largest psize that can safely be written\n</span><span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/ to an allocation of that size for this vdev.\n</span><span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/\n</span><span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/ Note that this function will not take into account the effect of gang\n</span><span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/ headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n</span><span class=\"syntax-comment syntax-line syntax-double-slash syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-js\">//</span>/ the psize_to_asize function.\n</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">fn</span> <span class=\"syntax-meta syntax-function-call syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">vdev_raidz_asize_to_psize</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vd</span>: <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">*</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vdev_t</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize</span>: <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">txg</span>: <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span> <span class=\"syntax-meta syntax-block syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-js\">{</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vdrz</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vd</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_tsd</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">ashift</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vd</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_top</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_ashift</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">cols</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vd_original_width</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">nparity</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vd_nparity</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">cols</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-function-call syntax-method syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">get_logical_width</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">txg</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n    <span class=\"syntax-meta syntax-function-call syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">assert</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">%</span> <span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-constant syntax-numeric syntax-js\">1</span> <span class=\"syntax-keyword syntax-operator syntax-bitwise syntax-js\">&lt;&lt;</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">ashift</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize_shifted</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize</span> <span class=\"syntax-keyword syntax-operator syntax-bitwise syntax-js\">&gt;&gt;</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">ashift</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">parity_adjusted</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize_shifted</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">-</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">nparity</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">*</span> <span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-meta syntax-function-call syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">divCeil</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize_shifted</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">cols</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">catch</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">unreachable</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">psize</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">parity_adjusted</span> <span class=\"syntax-keyword syntax-operator syntax-bitwise syntax-js\">&lt;&lt;</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">ashift</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n    <span class=\"syntax-keyword syntax-control syntax-flow syntax-js\">return</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n<span class=\"syntax-punctuation syntax-section syntax-block syntax-js\">}</span></span>\n</span></span></code></pre>\n<p>Let's start with the code formatting tool alone, <code>zig fmt</code>:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"JavaScript\"><span class=\"syntax-source syntax-js\"><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">andy</span>@<span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">bark</span> <span class=\"syntax-keyword syntax-operator syntax-bitwise syntax-js\">~</span><span class=\"syntax-string syntax-regexp syntax-js\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-js\">/</span>tmp&gt; zig fmt bug<span class=\"syntax-constant syntax-other syntax-character-class syntax-escape syntax-backslash syntax-regexp\">.</span>zig --ast-check\nbug<span class=\"syntax-constant syntax-other syntax-character-class syntax-escape syntax-backslash syntax-regexp\">.</span>zig:18:11: error: redeclaration of local constant &#39;cols&#39;\n    const cols = vdrz<span class=\"syntax-constant syntax-other syntax-character-class syntax-escape syntax-backslash syntax-regexp\">.</span>get_logical_width<span class=\"syntax-meta syntax-group syntax-regexp\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-regexp\">(</span>txg<span class=\"syntax-punctuation syntax-definition syntax-group syntax-regexp\">)</span></span>;\n          <span class=\"syntax-keyword syntax-control syntax-anchor syntax-regexp\">^</span>~~~\nbug<span class=\"syntax-constant syntax-other syntax-character-class syntax-escape syntax-backslash syntax-regexp\">.</span>zig:15:11: note: previous declaration here\n    const cols = vdrz<span class=\"syntax-constant syntax-other syntax-character-class syntax-escape syntax-backslash syntax-regexp\">.</span>vd_original_width;\n          <span class=\"syntax-keyword syntax-control syntax-anchor syntax-regexp\">^</span>~~~\n</span></span></span></code></pre>\n<p>Hmm, alright. That's definitely strange. The original blog post didn't notice that the initialization value of <code>cols</code> is dead code. That doesn't look like a logic error though. Let's fix it and try again:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"JavaScript\"><span class=\"syntax-source syntax-js\">@@ <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">-</span><span class=\"syntax-constant syntax-numeric syntax-js\">12</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span><span class=\"syntax-constant syntax-numeric syntax-js\">7</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">+</span><span class=\"syntax-constant syntax-numeric syntax-js\">12</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span><span class=\"syntax-constant syntax-numeric syntax-js\">6</span> @@\n <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">fn</span> <span class=\"syntax-meta syntax-function-call syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">vdev_raidz_asize_to_psize</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vd</span>: <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">*</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vdev_t</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">asize</span>: <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span><span class=\"syntax-punctuation syntax-separator syntax-comma syntax-js\">,</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">txg</span>: <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">u64</span> <span class=\"syntax-meta syntax-block syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-js\">{</span>\n     <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">vdrz</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vd</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_tsd</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n     <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">ashift</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vd</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_top</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vdev_ashift</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n<span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-js\">-</span>    <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">cols</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vd_original_width</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n     <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">nparity</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-property syntax-object syntax-js\">vd_nparity</span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n\n     <span class=\"syntax-storage syntax-type syntax-js\">const</span> <span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">cols</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-js\">=</span> <span class=\"syntax-variable syntax-other syntax-object syntax-js\">vdrz</span><span class=\"syntax-punctuation syntax-accessor syntax-js\">.</span><span class=\"syntax-meta syntax-function-call syntax-method syntax-js\"><span class=\"syntax-variable syntax-function syntax-js\">get_logical_width</span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">(</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-js\">txg</span></span><span class=\"syntax-meta syntax-group syntax-js\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-js\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-statement syntax-js\">;</span>\n</span></span></span></code></pre>\n<pre><code><span class=\"code-snippet\">andy@bark ~/tmp&gt; zig fmt bug.zig --ast-check\nbug.zig:23:11: error: unused local constant\n    const psize = parity_adjusted &lt;&lt; ashift;\n          ^~~~~\n</span></code></pre>\n<p>Bingo. We didn't even have to use the type checker.</p>\n<p>Thanks for reading my blog post.</p>\n",
  "content_markdown": "Someone on IRC shared this link with me: [An (almost) catastrophic OpenZFS bug and the humans that made it (and Rust is here too)](https://despairlabs.com/blog/posts/2025-07-10-an-openzfs-bug-and-the-humans-that-made-it/)\n\nThey wanted to know, would Zig catch this?\n\nI went through the trouble of porting the code snippet to Zig, so I thought I'd go the next step and turn it into a small blog post.\n\nOriginal snippet, in C:\n\n```\n/*\n * This code converts an asize into the largest psize that can safely be written\n * to an allocation of that size for this vdev.\n *\n * Note that this function will not take into account the effect of gang\n * headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n * the psize_to_asize function.\n */\nstatic uint64_t\nvdev_raidz_asize_to_psize(vdev_t *vd, uint64_t asize, uint64_t txg)\n{\n\tvdev_raidz_t *vdrz = vd->vdev_tsd;\n\tuint64_t psize;\n\tuint64_t ashift = vd->vdev_top->vdev_ashift;\n\tuint64_t cols = vdrz->vd_original_width;\n\tuint64_t nparity = vdrz->vd_nparity;\n\n\tcols = vdev_raidz_get_logical_width(vdrz, txg);\n\n\tASSERT0(asize % (1 << ashift));\n\n\tpsize = (asize >> ashift);\n\tpsize -= nparity * DIV_ROUND_UP(psize, cols);\n\tpsize <<= ashift;\n\n\treturn (asize);\n}\n```\n\nThe blog post author encourages us to try to [spot the fail](https://andrewkelley.me/post/spot-the-fail.html) before the answer is revealed.\n\nI couldn't do it before getting bored, so I ported the code to Zig instead:\n\n```\nconst std = @import(\"std\");\nconst divCeil = std.math.divCeil;\nconst assert = std.debug.assert;\nconst vdev_t = @import(\"the_rest_of_the_software.zig\").vdev_t;\n\n/// This code converts an asize into the largest psize that can safely be written\n/// to an allocation of that size for this vdev.\n///\n/// Note that this function will not take into account the effect of gang\n/// headers, which also modify the ASIZE of the DVAs. It is purely a reverse of\n/// the psize_to_asize function.\nfn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 {\n    const vdrz = vd.vdev_tsd;\n    const ashift = vd.vdev_top.vdev_ashift;\n    const cols = vdrz.vd_original_width;\n    const nparity = vdrz.vd_nparity;\n\n    const cols = vdrz.get_logical_width(txg);\n\n    assert(asize % (1 << ashift));\n\n    const asize_shifted = (asize >> ashift);\n    const parity_adjusted = asize_shifted - nparity * (divCeil(asize_shifted, cols) catch unreachable);\n    const psize = parity_adjusted << ashift;\n\n    return asize;\n}\n```\n\nLet's start with the code formatting tool alone, `zig fmt`:\n\n```\nandy@bark ~/tmp> zig fmt bug.zig --ast-check\nbug.zig:18:11: error: redeclaration of local constant 'cols'\n    const cols = vdrz.get_logical_width(txg);\n          ^~~~\nbug.zig:15:11: note: previous declaration here\n    const cols = vdrz.vd_original_width;\n          ^~~~\n```\n\nHmm, alright. That's definitely strange. The original blog post didn't notice that the initialization value of `cols` is dead code. That doesn't look like a logic error though. Let's fix it and try again:\n\n```\n@@ -12,7 +12,6 @@\n fn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 {\n     const vdrz = vd.vdev_tsd;\n     const ashift = vd.vdev_top.vdev_ashift;\n-    const cols = vdrz.vd_original_width;\n     const nparity = vdrz.vd_nparity;\n\n     const cols = vdrz.get_logical_width(txg);\n```\n\n```\nandy@bark ~/tmp> zig fmt bug.zig --ast-check\nbug.zig:23:11: error: unused local constant\n    const psize = parity_adjusted << ashift;\n          ^~~~~\n```\n\nBingo. We didn't even have to use the type checker.\n\nThanks for reading my blog post.\n",
  "dateCreated": "2026-09-19T21:30:23.395188495+00:00",
  "date_created": "2025-07-14T18:31:28+00:00",
  "date_modified": "2025-07-14T18:31:28+00:00",
  "date_published": "2025-07-14T18:31:28+00:00",
  "description": "OpenZFS Bug Ported to Zig Someone on IRC shared this link with me: An (almost) catastrophic OpenZFS bug and the humans that made it (and Rust is here too) They wanted to know, would Zig catch this? I went through the trouble of porting the…",
  "external_url": "https://andrewkelley.me/post/openzfs-bug-ported-zig.html",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/andrewkelley-me/2025/07/2025-07-14-openzfs-bug-ported-to-zig.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/andrewkelley-me/2025/07/2025-07-14-openzfs-bug-ported-to-zig.md"
  },
  "id": "urn:aggr:item:5080c170368e636c3644cef0e75cdf5d8a6591f2",
  "isBasedOn": {
    "@id": "https://andrewkelley.me/post/openzfs-bug-ported-zig.html"
  },
  "mainEntity": {
    "@id": "https://andrewkelley.me/post/openzfs-bug-ported-zig.html",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/andrewkelley-me/2025-07-14-openzfs-bug-ported-to-zig/#webpage"
    },
    "dateModified": "2025-07-14T18:31:28+00:00",
    "datePublished": "2025-07-14T18:31:28+00:00",
    "headline": "OpenZFS Bug Ported to Zig",
    "timeRequired": "PT3M",
    "url": "https://andrewkelley.me/post/openzfs-bug-ported-zig.html",
    "wordCount": 452
  },
  "name": "Archived snapshot: OpenZFS Bug Ported to Zig",
  "preview": null,
  "reading_minutes": 3,
  "replicated_at": null,
  "source": "andrewkelley-me",
  "source_name": "Andrew Kelley",
  "summary": "OpenZFS Bug Ported to Zig Someone on IRC shared this link with me: An (almost) catastrophic OpenZFS bug and the humans that made it (and Rust is here too) They wanted to know, would Zig catch this? I went through the trouble of porting the code snippet to Zig, so I thought I'd go the next step and turn it into a small blog post. Original snippet, in C: /* * This code converts an asize into the largest psize that can safely be written * to an allocation of that size for this vdev. * * Note that this function will not take into account the effect of gang * headers, which also modify the ASIZE of the DVAs. It is purely a reverse of * the psize_to_asize function. */ static uint64_t vdev_raidz_asize_to_psize(vdev_t *vd, uint64_t asize, uint64_t txg) { vdev_raidz_t *vdrz = vd->vdev_tsd; uint64_t psize; uint64_t ashift = vd->vdev_top->vdev_ashift; uint64_t cols = vdrz->vd_original_width; uint64_t nparity = vdrz->vd_nparity; cols = vdev_raidz_get_logical_width(vdrz, txg); ASSERT0(asize % (1 << ashift)); psize = (asize >> ashift); psize -= nparity * DIV_ROUND_UP(psize, cols); psize <<= ashift; return (asize); } The blog post author encourages us to try to spot the fail before the answer is revealed. I couldn't do it before getting bored, so I ported the code to Zig instead: const std = @import(\"std\"); const divCeil = std.math.divCeil; const assert = std.debug.assert; const vdev_t = @import(\"the_rest_of_the_software.zig\").vdev_t; /// This code converts an asize into the largest psize that can safely be written /// to an allocation of that size for this vdev. /// /// Note that this function will not take into account the effect of gang /// headers, which also modify the ASIZE of the DVAs. It is purely a reverse of /// the psize_to_asize function. fn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 { const vdrz = vd.vdev_tsd; const ashift = vd.vdev_top.vdev_ashift; const cols = vdrz.vd_original_width; const nparity = vdrz.vd_nparity; const cols = vdrz.get_logical_width(txg); assert(asize % (1 << ashift)); const asize_shifted = (asize >> ashift); const parity_adjusted = asize_shifted - nparity * (divCeil(asize_shifted, cols) catch unreachable); const psize = parity_adjusted << ashift; return asize; } Let's start with the code formatting tool alone, zig fmt: andy@bark ~/tmp> zig fmt bug.zig --ast-check bug.zig:18:11: error: redeclaration of local constant 'cols' const cols = vdrz.get_logical_width(txg); ^~~~ bug.zig:15:11: note: previous declaration here const cols = vdrz.vd_original_width; ^~~~ Hmm, alright. That's definitely strange. The original blog post didn't notice that the initialization value of cols is dead code. That doesn't look like a logic error though. Let's fix it and try again: @@ -12,7 +12,6 @@ fn vdev_raidz_asize_to_psize(vd: *vdev_t, asize: u64, txg: u64) u64 { const vdrz = vd.vdev_tsd; const ashift = vd.vdev_top.vdev_ashift; - const cols = vdrz.vd_original_width; const nparity = vdrz.vd_nparity; const cols = vdrz.get_logical_width(txg); andy@bark ~/tmp> zig fmt bug.zig --ast-check bug.zig:23:11: error: unused local constant const psize = parity_adjusted << ashift; ^~~~~ Bingo. We didn't even have to use the type checker.",
  "tags": [],
  "temporalCoverage": "2026-09-19T21:30:23.395188495+00:00",
  "title": "OpenZFS Bug Ported to Zig",
  "url": "https://aggr.aymericbeaumet.com/items/andrewkelley-me/2025-07-14-openzfs-bug-ported-to-zig/",
  "word_count": 452
}