Kernel: Update atime/ctime/mtime timestamps atomically

Instead of having three separate APIs (one for each timestamp),
there's now only Inode::update_timestamps() and it takes 3x optional
timestamps. The non-empty timestamps are updated while holding the inode
mutex, and the outside world no longer has to look at intermediate
timestamp states.
This commit is contained in:
Andreas Kling 2022-08-22 13:34:22 +02:00
parent 35b2e9c663
commit 280694bb46
Notes: sideshowbarker 2024-07-17 08:03:52 +09:00
18 changed files with 34 additions and 109 deletions

View file

@ -1537,34 +1537,23 @@ ErrorOr<NonnullLockRefPtr<Inode>> Ext2FSInode::lookup(StringView name)
return fs().get_inode({ fsid(), inode_index });
}
ErrorOr<void> Ext2FSInode::set_atime(time_t t)
ErrorOr<void> Ext2FSInode::update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime)
{
MutexLocker locker(m_inode_lock);
if (fs().is_readonly())
return EROFS;
if (t > INT32_MAX)
if (atime.value_or(0) > INT32_MAX)
return EINVAL;
m_raw_inode.i_atime = t;
set_metadata_dirty(true);
return {};
}
ErrorOr<void> Ext2FSInode::set_ctime(time_t t)
{
MutexLocker locker(m_inode_lock);
if (fs().is_readonly())
return EROFS;
m_raw_inode.i_ctime = t;
set_metadata_dirty(true);
return {};
}
ErrorOr<void> Ext2FSInode::set_mtime(time_t t)
{
MutexLocker locker(m_inode_lock);
if (fs().is_readonly())
return EROFS;
m_raw_inode.i_mtime = t;
if (ctime.value_or(0) > INT32_MAX)
return EINVAL;
if (mtime.value_or(0) > INT32_MAX)
return EINVAL;
if (atime.has_value())
m_raw_inode.i_atime = atime.value();
if (ctime.has_value())
m_raw_inode.i_ctime = ctime.value();
if (mtime.has_value())
m_raw_inode.i_mtime = mtime.value();
set_metadata_dirty(true);
return {};
}

View file

@ -44,9 +44,7 @@ private:
virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
virtual ErrorOr<void> add_child(Inode& child, StringView name, mode_t) override;
virtual ErrorOr<void> remove_child(StringView name) override;
virtual ErrorOr<void> set_atime(time_t) override;
virtual ErrorOr<void> set_ctime(time_t) override;
virtual ErrorOr<void> set_mtime(time_t) override;
virtual ErrorOr<void> update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime) override;
virtual ErrorOr<void> increment_link_count() override;
virtual ErrorOr<void> decrement_link_count() override;
virtual ErrorOr<void> chmod(mode_t) override;

View file

@ -528,17 +528,7 @@ ErrorOr<void> ISO9660Inode::truncate(u64)
return EROFS;
}
ErrorOr<void> ISO9660Inode::set_atime(time_t)
{
return EROFS;
}
ErrorOr<void> ISO9660Inode::set_ctime(time_t)
{
return EROFS;
}
ErrorOr<void> ISO9660Inode::set_mtime(time_t)
ErrorOr<void> ISO9660Inode::update_timestamps(Optional<time_t>, Optional<time_t>, Optional<time_t>)
{
return EROFS;
}

View file

@ -359,9 +359,7 @@ public:
virtual ErrorOr<void> chmod(mode_t) override;
virtual ErrorOr<void> chown(UserID, GroupID) override;
virtual ErrorOr<void> truncate(u64) override;
virtual ErrorOr<void> set_atime(time_t) override;
virtual ErrorOr<void> set_ctime(time_t) override;
virtual ErrorOr<void> set_mtime(time_t) override;
virtual ErrorOr<void> update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime) override;
private:
// HACK: The base ISO 9660 standard says the maximum filename length is 37

View file

@ -106,17 +106,7 @@ void Inode::will_be_destroyed()
(void)flush_metadata();
}
ErrorOr<void> Inode::set_atime(time_t)
{
return ENOTIMPL;
}
ErrorOr<void> Inode::set_ctime(time_t)
{
return ENOTIMPL;
}
ErrorOr<void> Inode::set_mtime(time_t)
ErrorOr<void> Inode::update_timestamps([[maybe_unused]] Optional<time_t> atime, [[maybe_unused]] Optional<time_t> ctime, [[maybe_unused]] Optional<time_t> mtime)
{
return ENOTIMPL;
}
@ -236,8 +226,7 @@ void Inode::did_modify_contents()
// FIXME: What happens if this fails?
// ENOTIMPL would be a meaningless error to return here
auto time = kgettimeofday().to_truncated_seconds();
(void)set_mtime(time);
(void)set_ctime(time);
(void)update_timestamps({}, time, time);
m_watchers.for_each([&](auto& watcher) {
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ContentModified);

View file

@ -80,9 +80,7 @@ public:
bool is_metadata_dirty() const { return m_metadata_dirty; }
virtual ErrorOr<void> set_atime(time_t);
virtual ErrorOr<void> set_ctime(time_t);
virtual ErrorOr<void> set_mtime(time_t);
virtual ErrorOr<void> update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime);
virtual ErrorOr<void> increment_link_count();
virtual ErrorOr<void> decrement_link_count();

View file

@ -49,7 +49,7 @@ ErrorOr<size_t> InodeFile::write(OpenFileDescription& description, u64 offset, U
nwritten = TRY(m_inode->write_bytes(offset, count, data, &description));
}
if (nwritten > 0) {
auto mtime_result = m_inode->set_mtime(kgettimeofday().to_truncated_seconds());
auto mtime_result = m_inode->update_timestamps({}, {}, kgettimeofday().to_truncated_seconds());
Thread::current()->did_file_write(nwritten);
evaluate_block_conditions();
if (mtime_result.is_error())
@ -106,7 +106,7 @@ ErrorOr<NonnullOwnPtr<KString>> InodeFile::pseudo_path(OpenFileDescription const
ErrorOr<void> InodeFile::truncate(u64 size)
{
TRY(m_inode->truncate(size));
TRY(m_inode->set_mtime(kgettimeofday().to_truncated_seconds()));
TRY(m_inode->update_timestamps({}, {}, kgettimeofday().to_truncated_seconds()));
return {};
}

View file

@ -145,11 +145,6 @@ ErrorOr<void> ProcFSGlobalInode::truncate(u64 size)
return m_associated_component->truncate(size);
}
ErrorOr<void> ProcFSGlobalInode::set_mtime(time_t time)
{
return m_associated_component->set_mtime(time);
}
InodeMetadata ProcFSGlobalInode::metadata() const
{
MutexLocker locker(m_inode_lock);

View file

@ -84,7 +84,6 @@ protected:
virtual ErrorOr<void> traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)>) const override;
virtual ErrorOr<NonnullLockRefPtr<Inode>> lookup(StringView) override;
virtual ErrorOr<void> truncate(u64) override final;
virtual ErrorOr<void> set_mtime(time_t) override final;
NonnullLockRefPtr<ProcFSExposedComponent> m_associated_component;
};

View file

@ -121,11 +121,6 @@ ErrorOr<void> SysFSInode::chown(UserID, GroupID)
return EPERM;
}
ErrorOr<void> SysFSInode::set_mtime(time_t time)
{
return m_associated_component->set_mtime(time);
}
ErrorOr<void> SysFSInode::truncate(u64 size)
{
return m_associated_component->truncate(size);

View file

@ -54,7 +54,6 @@ protected:
virtual ErrorOr<void> chmod(mode_t) override;
virtual ErrorOr<void> chown(UserID, GroupID) override;
virtual ErrorOr<void> truncate(u64) override;
virtual ErrorOr<void> set_mtime(time_t) override;
virtual ErrorOr<void> attach(OpenFileDescription& description) override final;
virtual void did_seek(OpenFileDescription&, off_t) override final;

View file

@ -35,7 +35,6 @@ public:
virtual mode_t permissions() const;
virtual ErrorOr<void> truncate(u64) { return EPERM; }
virtual size_t size() const { return 0; }
virtual ErrorOr<void> set_mtime(time_t) { return ENOTIMPL; }
virtual ErrorOr<size_t> write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) { return EROFS; }
virtual ErrorOr<void> refresh_data(OpenFileDescription&) const { return {}; }

View file

@ -27,7 +27,6 @@ public:
virtual mode_t permissions() const override;
virtual ErrorOr<size_t> write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override;
virtual ErrorOr<void> truncate(u64) override;
virtual ErrorOr<void> set_mtime(time_t) override { return {}; }
private:
PowerStateSwitchNode(FirmwareSysFSDirectory&);

View file

@ -301,29 +301,16 @@ ErrorOr<void> TmpFSInode::truncate(u64 size)
return {};
}
ErrorOr<void> TmpFSInode::set_atime(time_t time)
ErrorOr<void> TmpFSInode::update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime)
{
MutexLocker locker(m_inode_lock);
m_metadata.atime = time;
set_metadata_dirty(true);
return {};
}
ErrorOr<void> TmpFSInode::set_ctime(time_t time)
{
MutexLocker locker(m_inode_lock);
m_metadata.ctime = time;
set_metadata_dirty(true);
return {};
}
ErrorOr<void> TmpFSInode::set_mtime(time_t t)
{
MutexLocker locker(m_inode_lock);
m_metadata.mtime = t;
if (atime.has_value())
m_metadata.atime = atime.value();
if (ctime.has_value())
m_metadata.ctime = ctime.value();
if (mtime.has_value())
m_metadata.ctime = mtime.value();
set_metadata_dirty(true);
return {};
}

View file

@ -59,9 +59,7 @@ public:
virtual ErrorOr<void> chmod(mode_t) override;
virtual ErrorOr<void> chown(UserID, GroupID) override;
virtual ErrorOr<void> truncate(u64) override;
virtual ErrorOr<void> set_atime(time_t) override;
virtual ErrorOr<void> set_ctime(time_t) override;
virtual ErrorOr<void> set_mtime(time_t) override;
virtual ErrorOr<void> update_timestamps(Optional<time_t> atime, Optional<time_t> ctime, Optional<time_t> mtime) override;
private:
TmpFSInode(TmpFS& fs, InodeMetadata const& metadata, LockWeakPtr<TmpFSInode> parent);

View file

@ -213,8 +213,7 @@ ErrorOr<void> VirtualFileSystem::utime(Credentials const& credentials, StringVie
if (custody->is_readonly())
return EROFS;
TRY(inode.set_atime(atime));
TRY(inode.set_mtime(mtime));
TRY(inode.update_timestamps(atime, {}, mtime));
return {};
}
@ -228,10 +227,10 @@ ErrorOr<void> VirtualFileSystem::utimensat(Credentials const& credentials, Strin
return EROFS;
// NOTE: A standard ext2 inode cannot store nanosecond timestamps.
if (atime.tv_nsec != UTIME_OMIT)
TRY(inode.set_atime(atime.tv_sec));
if (mtime.tv_nsec != UTIME_OMIT)
TRY(inode.set_mtime(mtime.tv_sec));
TRY(inode.update_timestamps(
(atime.tv_nsec != UTIME_OMIT) ? atime.tv_nsec : Optional<time_t> {},
{},
(mtime.tv_nsec != UTIME_OMIT) ? mtime.tv_nsec : Optional<time_t> {}));
return {};
}
@ -321,7 +320,7 @@ ErrorOr<NonnullLockRefPtr<OpenFileDescription>> VirtualFileSystem::open(Credenti
if (should_truncate_file) {
TRY(inode.truncate(0));
TRY(inode.set_mtime(kgettimeofday().to_truncated_seconds()));
TRY(inode.update_timestamps({}, {}, kgettimeofday().to_truncated_seconds()));
}
auto description = TRY(OpenFileDescription::try_create(custody));
description->set_rw_mode(options);

View file

@ -184,11 +184,6 @@ ErrorOr<void> ProcFSSystemBoolean::truncate(u64 size)
return {};
}
ErrorOr<void> ProcFSSystemBoolean::set_mtime(time_t)
{
return {};
}
ErrorOr<size_t> ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const
{
VERIFY(offset == 0);

View file

@ -74,7 +74,6 @@ public:
virtual ErrorOr<NonnullLockRefPtr<ProcFSExposedComponent>> lookup(StringView) { VERIFY_NOT_REACHED(); };
virtual ErrorOr<size_t> write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) { return EROFS; }
virtual ErrorOr<void> truncate(u64) { return EPERM; }
virtual ErrorOr<void> set_mtime(time_t) { return ENOTIMPL; }
virtual mode_t required_mode() const { return 0444; }
virtual UserID owner_user() const { return 0; }
@ -201,7 +200,6 @@ private:
virtual ErrorOr<size_t> write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override final;
virtual mode_t required_mode() const override final { return 0644; }
virtual ErrorOr<void> truncate(u64) override final;
virtual ErrorOr<void> set_mtime(time_t) override final;
};
}