{"id":112696,"date":"2026-09-14T07:00:00","date_gmt":"2026-09-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112696"},"modified":"2026-09-14T07:17:26","modified_gmt":"2026-09-14T14:17:26","slug":"20260914-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260914-00\/?p=112696","title":{"rendered":"Why didn&#8217;t <CODE>Read&shy;Directory&shy;ChangesW<\/CODE> provide a way to correlate the two sides of a rename operation?"},"content":{"rendered":"<p>Brian Dellisanti asked <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260508-00\/?p=112310&amp;commentid=144190#comment-144190\"> why <code>Read\u00adDirectory\u00adChangesW<\/code> didn&#8217;t provide a way to correlate the two sides of a rename operation<\/a>.<\/p>\n<p>I wasn&#8217;t there, but I can guess.<\/p>\n<p>My guess is that the implementation always generated the two events one right after the other, so &#8220;obviously&#8221; the way you correlate them is to save the old name when you see the <code>FILE_<wbr \/>ACTION_<wbr \/>RENAMED_<wbr \/>OLD_<wbr \/>NAME<\/code>, and when the <code>FILE_<wbr \/>ACTION_<wbr \/>RENAMED_<wbr \/>NEW_<wbr \/>NAME<\/code> comes immediately after, you have your two sides.<\/p>\n<p>But they never wrote down that the two events always occur in direct succession. Which meant that when new file systems came along, they might not honor the unwritten rule. If two files are being renamed at the same time, is it possible that the two sets of rename events end up interleaved? There was nothing written down to forbid it, so I guess it&#8217;s possible.<\/p>\n<p>Note that I don&#8217;t know whether any file systems actually break this unwritten rule. From what I can tell, they do generate the two events in rapid succession, but rapid succession doesn&#8217;t <i>a priori<\/i> guarantee that they will come directly one after the other, particularly if there is a lot of concurrent disk activity going on.<\/p>\n<p>In practice, I couldn&#8217;t find a lot of code tracking renames anyway. They generally treated the <code>FILE_<wbr \/>ACTION_<wbr \/>RENAMED_<wbr \/>OLD_<wbr \/>NAME<\/code> as a deletion and the <code>FILE_<wbr \/>ACTION_<wbr \/>RENAMED_<wbr \/>NEW_<wbr \/>NAME<\/code> as a creation. And the ones that did track renames assumed that renames did not interleave. (Not that they had much choice.)<\/p>\n<p>I don&#8217;t think that providing the file IDs for the two sides of a rename operation was the purpose of <code>Read\u00adDirectory\u00adChanges\u00adExW<\/code>&#8216;s <code>Read\u00adDirectory\u00adNotify\u00adExtended\u00adInformation<\/code>. It was just a happy side effect that the extra information in the <code>Read\u00adDirectory\u00adNotify\u00adExtended\u00adInformation<\/code> also gives you the pieces needed to connect the dots reliably.<\/p>\n<p>I thought you might appreciate me pointing out the trick, that&#8217;s all.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Perhaps it wasn&#8217;t necessary, or perhaps it didn&#8217;t occur to them that this a problem.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-112696","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Perhaps it wasn&#8217;t necessary, or perhaps it didn&#8217;t occur to them that this a problem.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112696"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112696\/revisions"}],"predecessor-version":[{"id":112697,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112696\/revisions\/112697"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}