wolfcw / libfaketime

libfaketime modifies the system time for a single application
https://github.com/wolfcw/libfaketime
GNU General Public License v2.0
2.62k stars 319 forks source link

Reading back a timestamp that was written #454

Open mfeyg opened 5 months ago

mfeyg commented 5 months ago

I'm running into an inconsistency when using libfaketime.

If I set the last modified time on a file and then read it back, the time that comes back includes the offset from libfaketime rather than just being the time that I set. I would expect in the real future for the time to still come back as the time that was set on the file.

For example, in Java:

Files.setLastModifiedTime(someFile, FileTime.fromMillis(0));
Files.getLastModifiedTime(someFile).toMillis(); // is not 0

A possible mitigation (behind a flag?) would be to apply the reverse transformation when writing a time to the one that's applied when we read it.

That would mean that if I manually Files.setLastModifiedTime(someFile, FileTime.from(Instant.now())), the last modified time on the file system would still be the real current time because the system call to set the time would reverse the offset that was added by the system call to read the current time. But if my application tried to read the time itself, it would get the fake time.

Of course, if I depend on other processes seeing the fake time, this would defeat the purpose of the library. But losing the invariant that (if no one else is modifying the file) the time you read is the time you set leads to broken code.

wolfcw commented 5 months ago

Yes, it's inconsistent. But I'm afraid it's not easy to change (feel free to try though :-)).

libfaketime intercepts (hopefully most, ideally all) functions that read file timestamps. But it does not intercept all those functions that change files' timestamps (I assume you experimented with FAKE_FILE_TIMESTAMPS in the Makefile already, which addresses some of them). Considering timestamps such as atime (last access), this would probably include a lot of functions (such as fopen()) which otherwise are not time-related, and thus massively bloat libfaketime and reduce the performance of the programs it is applied to (as a lot more functions would have to be intercepted, mostly for doing nothing time-related).

Some timestamps may also be modified by the file system implementation in the kernel, to which libfaketime does not apply (it's only preloaded to the user-space application), so I think there would be inherent limits even if we intercept all those other functions exposed via syscalls/libc.

Finally, libfaketime currently tries to be as simple and stateless as possible. What you suggest would probably mean that libfaketime has to track which files have been written to (or one of the timestamps has been explicitly changed from user space otherwise), to only "reverse" the faked time for those but not all others.

If it affects only one or few files in an application of your own, a non-ideal workaround would be to deactivate libfaketime before changing a file's timestamp (e.g., by setting the FAKETIME environment variable to 0 with disabled libfaketime caching) and re-enabling it afterwards.

mfeyg commented 5 months ago

Considering timestamps such as atime (last access), this would probably include a lot of functions (such as fopen()) which otherwise are not time-related

I think what I'm suggesting is actually the reverse of what you're saying though.

Suppose it's January 1st and I set up libfaketime to pretend I'm a week in the future, which would be January 8th .

Rather than intercepting fopen calls to make it so that any file I access has an access time a week into the future, I'm suggesting to modify calls to explicitly set the modification time of a file to instead set the modification time a week earlier than the time I supply.

That way whether I actually modify the file, or just set the modification time to (what I think is) the current time, the effect will be the that the modification time of the file will remain January 1st. Then when I access the last modified time of the file—which libfaketime currently intercepts—I'll always see January 8th.

Currently, if I set the time explicitly, it'll be set to January 8th, and I'll read it as January 15th.

I admit that I have only a layman's understanding of the kernel interface, so the only reason I assume this is feasible is because I see that the time I'm reading is being modified, so I assume that explicit calls to write the time (ones that take in a timestamp) could be modified as well.

mfeyg commented 5 months ago

It's also possible that my understanding of the current state of affairs is off.

It looks like the way we have it set up currently, reading the modification time of a file does get intercepted and shifted by the offset. So if the offset is one week in the future, and I have a file that was last modified last Thursday, my process will see it having been modified next Thursday. This is what's causing the inconsistency.

Maybe the solution for us is to just turn faking file times off. I didn't set it up and didn't realize that was configurable. I think probably all we really need is to fake the current system time.