int (*getattr) (struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
- void (*update_time)(struct inode *, struct timespec *, int);
+ void (*update_time)(struct inode *inode, enum fs_update_time type,
+ int flags);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
int (*setattr) (struct mnt_idmap *, struct dentry *, struct iattr *);
int (*getattr) (struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
- void (*update_time)(struct inode *, struct timespec *, int);
+ void (*update_time)(struct inode *inode, enum fs_update_time type,
+ int flags);
int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode);
int (*tmpfile) (struct mnt_idmap *, struct inode *, struct file *, umode_t);
return -EIO;
}
-static int bad_inode_update_time(struct inode *inode, int flags)
+static int bad_inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
return -EIO;
}
* We need our own ->update_time so that we can return error on ENOSPC for
* updating the inode in the case of file write and mmap writes.
*/
-static int btrfs_update_time(struct inode *inode, int flags)
+static int btrfs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
- bool dirty;
+ int dirty;
if (btrfs_root_readonly(root))
return -EROFS;
- dirty = inode_update_timestamps(inode, flags);
- return dirty ? btrfs_dirty_inode(BTRFS_I(inode)) : 0;
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ return btrfs_dirty_inode(BTRFS_I(inode));
}
/*
#define FAT_UPDATE_CMTIME (1u << 1)
void fat_truncate_time(struct inode *inode, struct timespec64 *now,
unsigned int flags);
-extern int fat_update_time(struct inode *inode, int flags);
+int fat_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
int fat_cache_init(void);
}
EXPORT_SYMBOL_GPL(fat_truncate_time);
-int fat_update_time(struct inode *inode, int flags)
+int fat_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- unsigned int fat_flags = 0;
- int dirty_flags = 0;
-
- if (inode->i_ino == MSDOS_ROOT_INO)
- return 0;
-
- if (flags & S_ATIME)
- fat_flags |= FAT_UPDATE_ATIME;
- if (flags & (S_CTIME | S_MTIME))
- fat_flags |= FAT_UPDATE_CMTIME;
-
- if (fat_flags) {
- fat_truncate_time(inode, NULL, flags);
- if (inode->i_sb->s_flags & SB_LAZYTIME)
- dirty_flags |= I_DIRTY_TIME;
- else
- dirty_flags |= I_DIRTY_SYNC;
+ if (inode->i_ino != MSDOS_ROOT_INO) {
+ fat_truncate_time(inode, NULL, type == FS_UPD_ATIME ?
+ FAT_UPDATE_ATIME : FAT_UPDATE_CMTIME);
+ __mark_inode_dirty(inode, inode_time_dirty_flag(inode));
}
-
- __mark_inode_dirty(inode, dirty_flags);
return 0;
}
EXPORT_SYMBOL_GPL(fat_update_time);
return vfs_setpos(file, ret, inode->i_sb->s_maxbytes);
}
-static int gfs2_update_time(struct inode *inode, int flags)
+static int gfs2_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_glock *gl = ip->i_gl;
if (error)
return error;
}
- return generic_update_time(inode, flags);
+ return generic_update_time(inode, type, flags);
}
static const struct inode_operations gfs2_file_iops = {
return false;
}
+static int inode_update_atime(struct inode *inode)
+{
+ struct timespec64 atime = inode_get_atime(inode);
+ struct timespec64 now = current_time(inode);
+
+ if (timespec64_equal(&now, &atime))
+ return 0;
+
+ inode_set_atime_to_ts(inode, now);
+ return inode_time_dirty_flag(inode);
+}
+
+static int inode_update_cmtime(struct inode *inode)
+{
+ struct timespec64 ctime = inode_get_ctime(inode);
+ struct timespec64 mtime = inode_get_mtime(inode);
+ struct timespec64 now = inode_set_ctime_current(inode);
+ unsigned int dirty = 0;
+ bool mtime_changed;
+
+ mtime_changed = !timespec64_equal(&now, &mtime);
+ if (mtime_changed || !timespec64_equal(&now, &ctime))
+ dirty = inode_time_dirty_flag(inode);
+ if (mtime_changed)
+ inode_set_mtime_to_ts(inode, now);
+
+ if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, !!dirty))
+ dirty |= I_DIRTY_SYNC;
+
+ return dirty;
+}
+
/**
- * inode_update_timestamps - update the timestamps on the inode
+ * inode_update_time - update either atime or c/mtime and i_version on the inode
* @inode: inode to be updated
- * @flags: S_* flags that needed to be updated
+ * @type: timestamp to be updated
+ * @flags: flags for the update
*
- * The update_time function is called when an inode's timestamps need to be
- * updated for a read or write operation. This function handles updating the
- * actual timestamps. It's up to the caller to ensure that the inode is marked
- * dirty appropriately.
+ * Update either atime or c/mtime and version in a inode if needed for a file
+ * access or modification. It is up to the caller to mark the inode dirty
+ * appropriately.
*
- * In the case where any of S_MTIME, S_CTIME, or S_VERSION need to be updated,
- * attempt to update all three of them. S_ATIME updates can be handled
- * independently of the rest.
- *
- * Returns a set of S_* flags indicating which values changed.
+ * Returns the positive I_DIRTY_* flags for __mark_inode_dirty() if the inode
+ * needs to be marked dirty, 0 if it did not, or a negative errno if an error
+ * happened.
*/
-int inode_update_timestamps(struct inode *inode, int flags)
+int inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- int updated = 0;
- struct timespec64 now;
-
- if (flags & (S_MTIME|S_CTIME|S_VERSION)) {
- struct timespec64 ctime = inode_get_ctime(inode);
- struct timespec64 mtime = inode_get_mtime(inode);
-
- now = inode_set_ctime_current(inode);
- if (!timespec64_equal(&now, &ctime))
- updated |= S_CTIME;
- if (!timespec64_equal(&now, &mtime)) {
- inode_set_mtime_to_ts(inode, now);
- updated |= S_MTIME;
- }
- if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, updated))
- updated |= S_VERSION;
- } else {
- now = current_time(inode);
- }
-
- if (flags & S_ATIME) {
- struct timespec64 atime = inode_get_atime(inode);
-
- if (!timespec64_equal(&now, &atime)) {
- inode_set_atime_to_ts(inode, now);
- updated |= S_ATIME;
- }
+ switch (type) {
+ case FS_UPD_ATIME:
+ return inode_update_atime(inode);
+ case FS_UPD_CMTIME:
+ return inode_update_cmtime(inode);
+ default:
+ WARN_ON_ONCE(1);
+ return -EIO;
}
- return updated;
}
-EXPORT_SYMBOL(inode_update_timestamps);
+EXPORT_SYMBOL(inode_update_time);
/**
* generic_update_time - update the timestamps on the inode
* @inode: inode to be updated
- * @flags: S_* flags that needed to be updated
- *
- * The update_time function is called when an inode's timestamps need to be
- * updated for a read or write operation. In the case where any of S_MTIME, S_CTIME,
- * or S_VERSION need to be updated we attempt to update all three of them. S_ATIME
- * updates can be handled done independently of the rest.
+ * @type: timestamp to be updated
+ * @flags: flags for the update
*
* Returns a negative error value on error, else 0.
*/
-int generic_update_time(struct inode *inode, int flags)
+int generic_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- int updated = inode_update_timestamps(inode, flags);
- int dirty_flags = 0;
+ int dirty;
- if (updated & (S_ATIME|S_MTIME|S_CTIME))
- dirty_flags = inode->i_sb->s_flags & SB_LAZYTIME ? I_DIRTY_TIME : I_DIRTY_SYNC;
- if (updated & S_VERSION)
- dirty_flags |= I_DIRTY_SYNC;
- __mark_inode_dirty(inode, dirty_flags);
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ __mark_inode_dirty(inode, dirty);
return 0;
}
EXPORT_SYMBOL(generic_update_time);
* of the fs read only, e.g. subvolumes in Btrfs.
*/
if (inode->i_op->update_time)
- inode->i_op->update_time(inode, S_ATIME);
+ inode->i_op->update_time(inode, FS_UPD_ATIME, 0);
else
- generic_update_time(inode, S_ATIME);
+ generic_update_time(inode, FS_UPD_ATIME, 0);
mnt_put_write_access(mnt);
skip_update:
sb_end_write(inode->i_sb);
{
struct inode *inode = file_inode(file);
struct timespec64 now, ts;
- int sync_mode = 0;
+ bool need_update = false;
int ret = 0;
/* First try to exhaust all avenues to not sync */
ts = inode_get_mtime(inode);
if (!timespec64_equal(&ts, &now))
- sync_mode |= S_MTIME;
+ need_update = true;
ts = inode_get_ctime(inode);
if (!timespec64_equal(&ts, &now))
- sync_mode |= S_CTIME;
+ need_update = true;
if (IS_I_VERSION(inode) && inode_iversion_need_inc(inode))
- sync_mode |= S_VERSION;
+ need_update = true;
- if (!sync_mode)
+ if (!need_update)
return 0;
if (flags & IOCB_NOWAIT)
if (mnt_get_write_access_file(file))
return 0;
if (inode->i_op->update_time)
- ret = inode->i_op->update_time(inode, sync_mode);
+ ret = inode->i_op->update_time(inode, FS_UPD_CMTIME, 0);
else
- ret = generic_update_time(inode, sync_mode);
+ ret = generic_update_time(inode, FS_UPD_CMTIME, 0);
mnt_put_write_access_file(file);
return ret;
}
struct timespec64 ctime = inode_get_ctime(inode);
struct timespec64 mtime = inode_get_mtime(inode);
struct timespec64 now;
- int updated = 0;
+ bool updated = false;
now = inode_set_ctime_current(inode);
if (!timespec64_equal(&now, &ctime))
- updated |= S_CTIME;
+ updated = true;
inode_set_mtime_to_ts(inode, attr->ia_mtime);
if (!timespec64_equal(&now, &mtime))
- updated |= S_MTIME;
+ updated = true;
inode_maybe_inc_iversion(inode, updated);
cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
static void nfs_update_atime(struct inode *inode)
{
- inode_update_timestamps(inode, S_ATIME);
+ inode_update_time(inode, FS_UPD_ATIME, 0);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ATIME;
}
static void nfs_update_mtime(struct inode *inode)
{
- inode_update_timestamps(inode, S_MTIME | S_CTIME);
+ inode_update_time(inode, FS_UPD_CMTIME, 0);
NFS_I(inode)->cache_validity &=
~(NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME);
}
return generic_permission(&nop_mnt_idmap, inode, mask);
}
-int orangefs_update_time(struct inode *inode, int flags)
+int orangefs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- struct iattr iattr;
+ struct iattr iattr = { };
+ int dirty;
- gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_update_time: %pU\n",
- get_khandle_from_ino(inode));
-
- flags = inode_update_timestamps(inode, flags);
+ switch (type) {
+ case FS_UPD_ATIME:
+ iattr.ia_valid = ATTR_ATIME;
+ break;
+ case FS_UPD_CMTIME:
+ iattr.ia_valid = ATTR_CTIME | ATTR_MTIME;
+ break;
+ }
- memset(&iattr, 0, sizeof iattr);
- if (flags & S_ATIME)
- iattr.ia_valid |= ATTR_ATIME;
- if (flags & S_CTIME)
- iattr.ia_valid |= ATTR_CTIME;
- if (flags & S_MTIME)
- iattr.ia_valid |= ATTR_MTIME;
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
return __orangefs_setattr(inode, &iattr);
}
int orangefs_permission(struct mnt_idmap *idmap,
struct inode *inode, int mask);
-int orangefs_update_time(struct inode *, int);
+int orangefs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/*
* defined in xattr.c
}
#endif
-int ovl_update_time(struct inode *inode, int flags)
+int ovl_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- if (flags & S_ATIME) {
+ if (type == FS_UPD_ATIME) {
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
struct path upperpath = {
.mnt = ovl_upper_mnt(ofs),
}
#endif
-int ovl_update_time(struct inode *inode, int flags);
+int ovl_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
struct ovl_inode_params {
return 0;
}
-/**
- * ubifs_update_time - update time of inode.
- * @inode: inode to update
- * @flags: time updating control flag determines updating
- * which time fields of @inode
- *
- * This function updates time of the inode.
- *
- * Returns: %0 for success or a negative error code otherwise.
- */
-int ubifs_update_time(struct inode *inode, int flags)
+int ubifs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct ubifs_inode *ui = ubifs_inode(inode);
struct ubifs_info *c = inode->i_sb->s_fs_info;
.dirtied_ino_d = ALIGN(ui->data_len, 8) };
int err, release;
+ /* ubifs sets S_NOCMTIME on all inodes, this should not happen. */
+ if (WARN_ON_ONCE(type != FS_UPD_ATIME))
+ return -EIO;
+
if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
- return generic_update_time(inode, flags);
+ return generic_update_time(inode, type, flags);
err = ubifs_budget_space(c, &req);
if (err)
return err;
mutex_lock(&ui->ui_mutex);
- inode_update_timestamps(inode, flags);
+ inode_update_time(inode, type, flags);
release = ui->dirty;
__mark_inode_dirty(inode, I_DIRTY_SYNC);
mutex_unlock(&ui->ui_mutex);
int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
int ubifs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr);
-int ubifs_update_time(struct inode *inode, int flags);
+int ubifs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/* dir.c */
struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
STATIC int
xfs_vn_update_time(
struct inode *inode,
- int flags)
+ enum fs_update_time type,
+ unsigned int flags)
{
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
int log_flags = XFS_ILOG_TIMESTAMP;
struct xfs_trans *tp;
int error;
- struct timespec64 now;
trace_xfs_update_time(ip);
if (inode->i_sb->s_flags & SB_LAZYTIME) {
- if (!((flags & S_VERSION) &&
- inode_maybe_inc_iversion(inode, false)))
- return generic_update_time(inode, flags);
+ if (type == FS_UPD_ATIME ||
+ !inode_maybe_inc_iversion(inode, false))
+ return generic_update_time(inode, type, flags);
/* Capture the iversion update that just occurred */
log_flags |= XFS_ILOG_CORE;
return error;
xfs_ilock(ip, XFS_ILOCK_EXCL);
- if (flags & (S_CTIME|S_MTIME))
- now = inode_set_ctime_current(inode);
+ if (type == FS_UPD_ATIME)
+ inode_set_atime_to_ts(inode, current_time(inode));
else
- now = current_time(inode);
-
- if (flags & S_MTIME)
- inode_set_mtime_to_ts(inode, now);
- if (flags & S_ATIME)
- inode_set_atime_to_ts(inode, now);
-
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_log_inode(tp, ip, log_flags);
return xfs_trans_commit(tp);
struct timespec64 simple_inode_init_ts(struct inode *inode);
+static inline int inode_time_dirty_flag(struct inode *inode)
+{
+ if (inode->i_sb->s_flags & SB_LAZYTIME)
+ return I_DIRTY_TIME;
+ return I_DIRTY_SYNC;
+}
+
/*
* Snapshotting support.
*/
static int shared_##x(struct file *file , struct dir_context *ctx) \
{ return wrap_directory_iterator(file, ctx, x); }
+enum fs_update_time {
+ FS_UPD_ATIME,
+ FS_UPD_CMTIME,
+};
+
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
- int (*update_time)(struct inode *, int);
+ int (*update_time)(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
mark_inode_dirty(inode);
}
-enum file_time_flags {
- S_ATIME = 1,
- S_MTIME = 2,
- S_CTIME = 4,
- S_VERSION = 8,
-};
-
extern bool atime_needs_update(const struct path *, struct inode *);
extern void touch_atime(const struct path *);
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
void iput_not_last(struct inode *);
-int inode_update_timestamps(struct inode *inode, int flags);
-int generic_update_time(struct inode *inode, int flags);
+int inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
+int generic_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/* /sys/fs */
extern struct kobject *fs_kobj;