Bug 241373 - [UFS] Rename should update the ctime of a multiply-linked destination
Summary: [UFS] Rename should update the ctime of a multiply-linked destination
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Many People
Assignee: freebsd-fs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-10-20 15:21 UTC by Alan Somers
Modified: 2019-10-29 22:05 UTC (History)
1 user (show)

See Also:


Attachments
Regression test (844 bytes, text/plain)
2019-10-20 15:21 UTC, Alan Somers
no flags Details
Version of the test case that also works on 11.2-STABLE (1.01 KB, text/plain)
2019-10-21 18:18 UTC, Alan Somers
no flags Details
fix to avoid delayed ctime update (609 bytes, patch)
2019-10-22 06:10 UTC, Kirk McKusick
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alan Somers freebsd_committer freebsd_triage 2019-10-20 15:21:50 UTC
Created attachment 208465 [details]
Regression test

ctime should be updated whenever a file's link count changes. If its destination is multiply-linked, rename(2) will decrease its link count, and therefore should update its ctime.  ZFS complies, but as of r353698 UFS does not.  I have two test cases demonstrating the problem: a standalone C program (attached), and a pjdfstest test case (https://github.com/pjd/pjdfstest/pull/37).

Standalone test's output on UFS:

$ /tmp/rename_ctime_link
nlink: 2 -> 1    ctime 1571584801.941284000 -> 1571584801.941284000
Renamed failed to update destination's ctime.

And on ZFS:

$ /tmp/rename_ctime_link 
nlink: 2 -> 1    ctime 1571584883.332680000 -> 1571584884.384654000
Everything ok!

$ uname -a
FreeBSD fbsd-head.lauralan.noip.me 13.0-CURRENT FreeBSD 13.0-CURRENT #50 r353698: Thu Oct 17 15:17:38 MDT 2019     somers@fbsd-head.lauralan.noip.me:/usr/obj/usr/home/somers/freebsd/base/head/amd64.amd64/sys/GENERIC  amd64
$ mount
/dev/vtbd0p2 on / (ufs, local, journaled soft-updates)
devfs on /dev (devfs)
192.168.0.2:/home on /usr/home (nfs, nfsv4acls)
tank on /tank (zfs, local, nfsv4acls)
Comment 1 Kirk McKusick freebsd_committer freebsd_triage 2019-10-21 04:45:42 UTC
Could you bisect to find the specific change that caused this breakage?
Comment 2 Alan Somers freebsd_committer freebsd_triage 2019-10-21 13:59:39 UTC
Kirk, are you sure that it ever worked?  I'm not.  In case I wasn't clear, my two test cases are freshly written.
Comment 3 Kirk McKusick freebsd_committer freebsd_triage 2019-10-21 17:50:30 UTC
(In reply to Alan Somers from comment #2)
OK, I thought that you were reporting that something had stopped working. Do you have access to an older system (11.X say) that you can test?
Comment 4 Alan Somers freebsd_committer freebsd_triage 2019-10-21 18:17:00 UTC
The oldest VM I have is 11.2-STABLE #0 r339079.  I just checked there, and the test still fails.
Comment 5 Alan Somers freebsd_committer freebsd_triage 2019-10-21 18:18:37 UTC
Created attachment 208490 [details]
Version of the test case that also works on 11.2-STABLE
Comment 6 Kirk McKusick freebsd_committer freebsd_triage 2019-10-22 06:10:07 UTC
Created attachment 208499 [details]
fix to avoid delayed ctime update
Comment 7 Kirk McKusick freebsd_committer freebsd_triage 2019-10-22 06:12:53 UTC
Your test passes on a filesystem without soft updates which was my first clue. It also passes if you add to your test program a usleep(1000000) call after the call to rename() which was the second clue. When running with soft updates, the ctime is not updated until the soft updates background process has settled all the needed I/O operations. Your test program stat()s it immediately after the rename() has returned, but soft updates has not finished with the processing, so the ctime has not yet been updated.

The attached patch causes the ctime to be updated immediately during the rename() which makes your test happy. The ctime will be updated again when soft updates has finished its processing because that is the time that is correct from the perspective of programs that look at the disk (like dump). This change does not cause any extra I/O to be done, it just ensures that stat() updates the ctime before handing it back to you.
Comment 8 Kirk McKusick freebsd_committer freebsd_triage 2019-10-22 23:34:29 UTC
(In reply to Alan Somers from comment #5)
Let me know if my patch solves your problem.
Comment 9 Alan Somers freebsd_committer freebsd_triage 2019-10-23 01:21:47 UTC
Works for me!
Comment 10 commit-hook freebsd_committer freebsd_triage 2019-10-24 21:29:29 UTC
A commit references this bug:

Author: mckusick
Date: Thu Oct 24 21:28:37 UTC 2019
New revision: 354050
URL: https://svnweb.freebsd.org/changeset/base/354050

Log:
  After the unlink() of one name of a file with multiple links, a
  stat() of one of the remaining names of the file does not show an
  updated ctime (inode modification time) until several seconds after
  the unlink() completes. The problem only occurs when the filesystem
  is running with soft updates enabled. When running with soft updates,
  the ctime is not updated until the soft updates background process
  has settled all the needed I/O operations.

  This commit causes the ctime to be updated immediately during the
  unlink(). A side effect of this change is that the ctime is updated
  again when soft updates has finished its processing because that
  is the time that is correct from the perspective of programs that
  look at the disk (like dump). This change does not cause any extra
  I/O to be done, it just ensures that stat() updates the ctime before
  handing it back.

  PR:           241373
  Reported by:  Alan Somers
  Tested by:    Alan Somers
  MFC after:    3 days
  Sponsored by: Netflix

Changes:
  head/sys/ufs/ufs/ufs_lookup.c
Comment 11 Kirk McKusick freebsd_committer freebsd_triage 2019-10-29 22:05:24 UTC
MFC'ed to 11-stable and 12-stable