VFS currently uses struct timespec timestamps which are not y2038 safe.
Change all the struct inode timestamps accesses through accessor functions only. This will help the switch over to 64 bit times seamlessly.
Change all struct iattr and struct kstat timestamps to use struct inode_timespec. This will use struct timespec64 when 64 bit time switch occurs.
Change all calls to CURRENT_TIME to current_fs_time(). The CURRENT_TIME macro is not accurate for file system code as it does not perform range checks on timestamps nor does it cater to individual file system timestamp granularity. Change all calls to timespec_trunc() to fs_time_trunc(). The later will be extended to support range checking on timestamps.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- fs/attr.c | 14 +++++++------- fs/bad_inode.c | 9 +++++++-- fs/binfmt_misc.c | 7 +++++-- fs/inode.c | 44 ++++++++++++++++++++++++++---------------- fs/libfs.c | 58 +++++++++++++++++++++++++++++++++++++++++--------------- fs/locks.c | 5 ++--- fs/nsfs.c | 6 +++++- fs/pipe.c | 6 +++++- fs/posix_acl.c | 2 +- fs/stat.c | 6 +++--- fs/utimes.c | 4 ++-- 11 files changed, 108 insertions(+), 53 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c index 6530ced..d42af4c 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -148,14 +148,14 @@ void setattr_copy(struct inode *inode, const struct iattr *attr) if (ia_valid & ATTR_GID) inode->i_gid = attr->ia_gid; if (ia_valid & ATTR_ATIME) - inode->i_atime = timespec_trunc(attr->ia_atime, - inode->i_sb->s_time_gran); + inode_set_inode_time(&inode->i_atime, fs_time_trunc(attr->ia_atime, + inode->i_sb)); if (ia_valid & ATTR_MTIME) - inode->i_mtime = timespec_trunc(attr->ia_mtime, - inode->i_sb->s_time_gran); + inode_set_inode_time(&inode->i_mtime, fs_time_trunc(attr->ia_mtime, + inode->i_sb)); if (ia_valid & ATTR_CTIME) - inode->i_ctime = timespec_trunc(attr->ia_ctime, - inode->i_sb->s_time_gran); + inode_set_inode_time(&inode->i_ctime, fs_time_trunc(attr->ia_ctime, + inode->i_sb)); if (ia_valid & ATTR_MODE) { umode_t mode = attr->ia_mode;
@@ -192,7 +192,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; int error; - struct timespec now; + struct inode_timespec now; unsigned int ia_valid = attr->ia_valid;
WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex)); diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 103f5d7..20cf831 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -169,11 +169,16 @@ static const struct inode_operations bad_inode_ops =
void make_bad_inode(struct inode *inode) { + struct inode_timespec now; + remove_inode_hash(inode);
inode->i_mode = S_IFREG; - inode->i_atime = inode->i_mtime = inode->i_ctime = - current_fs_time(inode->i_sb); + + now = current_fs_time(inode->i_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now); inode->i_op = &bad_inode_ops; inode->i_fop = &bad_file_ops; } diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 78f005f..48ae5ff 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -562,12 +562,15 @@ static void entry_status(Node *e, char *page) static struct inode *bm_get_inode(struct super_block *sb, int mode) { struct inode *inode = new_inode(sb); + struct inode_timespec now;
if (inode) { inode->i_ino = get_next_ino(); inode->i_mode = mode; - inode->i_atime = inode->i_mtime = inode->i_ctime = - current_fs_time(inode->i_sb); + now = current_fs_time(inode->i_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now); } return inode; } diff --git a/fs/inode.c b/fs/inode.c index 59e55ee..0318472 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1532,27 +1532,36 @@ EXPORT_SYMBOL(bmap); * passed since the last atime update. */ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, - struct timespec now) + struct inode_timespec now) {
+ struct inode_timespec ctime; + struct inode_timespec mtime; + struct inode_timespec atime; + if (!(mnt->mnt_flags & MNT_RELATIME)) return 1; + + atime = inode_get_inode_time(&inode->i_atime); + ctime = inode_get_inode_time(&inode->i_ctime); + mtime = inode_get_inode_time(&inode->i_mtime); + /* * Is mtime younger than atime? If yes, update atime: */ - if (timespec_compare(&inode->i_mtime, &inode->i_atime) >= 0) + if (inode_timespec_compare(&mtime, &atime) >= 0) return 1; /* * Is ctime younger than atime? If yes, update atime: */ - if (timespec_compare(&inode->i_ctime, &inode->i_atime) >= 0) + if (inode_timespec_compare(&ctime, &atime) >= 0) return 1;
/* * Is the previous atime value older than a day? If yes, * update atime: */ - if ((long)(now.tv_sec - inode->i_atime.tv_sec) >= 24*60*60) + if ((long)(now.tv_sec - atime.tv_sec) >= 24*60*60) return 1; /* * Good, we can skip the atime update: @@ -1560,18 +1569,18 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, return 0; }
-int generic_update_time(struct inode *inode, struct timespec *time, int flags) +int generic_update_time(struct inode *inode, struct inode_timespec *time, int flags) { int iflags = I_DIRTY_TIME;
if (flags & S_ATIME) - inode->i_atime = *time; + inode_set_inode_time(&inode->i_atime, *time); if (flags & S_VERSION) inode_inc_iversion(inode); if (flags & S_CTIME) - inode->i_ctime = *time; + inode_set_inode_time(&inode->i_ctime, *time); if (flags & S_MTIME) - inode->i_mtime = *time; + inode_set_inode_time(&inode->i_mtime, *time);
if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION)) iflags |= I_DIRTY_SYNC; @@ -1584,9 +1593,9 @@ EXPORT_SYMBOL(generic_update_time); * This does the actual work of updating an inodes time or version. Must have * had called mnt_want_write() before calling this. */ -static int update_time(struct inode *inode, struct timespec *time, int flags) +static int update_time(struct inode *inode, struct inode_timespec *time, int flags) { - int (*update_time)(struct inode *, struct timespec *, int); + int (*update_time)(struct inode *, struct inode_timespec *, int);
update_time = inode->i_op->update_time ? inode->i_op->update_time : generic_update_time; @@ -1606,7 +1615,8 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) bool atime_needs_update(const struct path *path, struct inode *inode) { struct vfsmount *mnt = path->mnt; - struct timespec now; + struct inode_timespec now; + struct inode_timespec atime;
if (inode->i_flags & S_NOATIME) return false; @@ -1625,7 +1635,8 @@ bool atime_needs_update(const struct path *path, struct inode *inode) if (!relatime_need_update(mnt, inode, now)) return false;
- if (timespec_equal(&inode->i_atime, &now)) + atime = inode_get_inode_time(&inode->i_atime); + if (inode_timespec_equal(&atime, &now)) return false;
return true; @@ -1635,7 +1646,7 @@ void touch_atime(const struct path *path) { struct vfsmount *mnt = path->mnt; struct inode *inode = d_inode(path->dentry); - struct timespec now; + struct inode_timespec now;
if (!atime_needs_update(path, inode)) return; @@ -1770,7 +1781,8 @@ EXPORT_SYMBOL(file_remove_privs); int file_update_time(struct file *file) { struct inode *inode = file_inode(file); - struct timespec now; + struct inode_timespec now; + int sync_it = 0; int ret;
@@ -1779,10 +1791,10 @@ int file_update_time(struct file *file) return 0;
now = current_fs_time(inode->i_sb); - if (!timespec_equal(&inode->i_mtime, &now)) + if (!inode_timespec_equal(&inode->i_mtime, &now)) sync_it = S_MTIME;
- if (!timespec_equal(&inode->i_ctime, &now)) + if (!inode_timespec_equal(&inode->i_ctime, &now)) sync_it |= S_CTIME;
if (IS_I_VERSION(inode)) diff --git a/fs/libfs.c b/fs/libfs.c index c7cbfb0..dc68627 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -216,6 +216,7 @@ struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name, struct dentry *dentry; struct inode *root; struct qstr d_name = QSTR_INIT(name, strlen(name)); + struct inode_timespec now;
s = sget(fs_type, NULL, set_anon_super, MS_NOUSER, NULL); if (IS_ERR(s)) @@ -237,7 +238,11 @@ struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name, */ root->i_ino = 1; root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; - root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; + now = current_fs_time(s); + inode_set_inode_time(&root->i_atime, now); + inode_set_inode_time(&root->i_ctime, now); + inode_set_inode_time(&root->i_mtime, now); + dentry = __d_alloc(s, &d_name); if (!dentry) { iput(root); @@ -266,8 +271,11 @@ EXPORT_SYMBOL(simple_open); int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(old_dentry); + struct inode_timespec now = current_fs_time(inode->i_sb);
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + inode_set_inode_time(&inode->i_ctime, now); + inode_set_inode_time(&dir->i_mtime, now); + inode_set_inode_time(&dir->i_ctime, now); inc_nlink(inode); ihold(inode); dget(dentry); @@ -300,8 +308,11 @@ EXPORT_SYMBOL(simple_empty); int simple_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(dentry); + struct inode_timespec now = current_fs_time(inode->i_sb);
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + inode_set_inode_time(&inode->i_ctime, now); + inode_set_inode_time(&dir->i_mtime, now); + inode_set_inode_time(&dir->i_ctime, now); drop_nlink(inode); dput(dentry); return 0; @@ -325,6 +336,7 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *inode = d_inode(old_dentry); int they_are_dirs = d_is_dir(old_dentry); + struct inode_timespec now;
if (!simple_empty(new_dentry)) return -ENOTEMPTY; @@ -340,8 +352,12 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, inc_nlink(new_dir); }
- old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = - new_dir->i_mtime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(inode->i_sb); + inode_set_inode_time(&old_dir->i_ctime, now); + inode_set_inode_time(&old_dir->i_mtime, now); + inode_set_inode_time(&new_dir->i_ctime, now); + inode_set_inode_time(&new_dir->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now);
return 0; } @@ -415,13 +431,13 @@ EXPORT_SYMBOL(simple_write_begin); /** * simple_write_end - .write_end helper for non-block-device FSes * @available: See .write_end of address_space_operations - * @file: " - * @mapping: " - * @pos: " - * @len: " - * @copied: " - * @page: " - * @fsdata: " + * @file: " + * @mapping: " + * @pos: " + * @len: " + * @copied: " + * @page: " + * @fsdata: " * * simple_write_end does the minimum needed for updating a page after writing is * done. It has the same API signature as the .write_end of @@ -475,6 +491,7 @@ int simple_fill_super(struct super_block *s, unsigned long magic, struct inode *inode; struct dentry *root; struct dentry *dentry; + struct inode_timespec now; int i;
s->s_blocksize = PAGE_CACHE_SIZE; @@ -492,7 +509,10 @@ int simple_fill_super(struct super_block *s, unsigned long magic, */ inode->i_ino = 1; inode->i_mode = S_IFDIR | 0755; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(inode->i_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now); inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; set_nlink(inode, 2); @@ -518,7 +538,10 @@ int simple_fill_super(struct super_block *s, unsigned long magic, goto out; } inode->i_mode = S_IFREG | files->mode; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(inode->i_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now); inode->i_fop = files->ops; inode->i_ino = i; d_add(dentry, inode); @@ -1051,6 +1074,7 @@ struct inode *alloc_anon_inode(struct super_block *s) .set_page_dirty = anon_set_page_dirty, }; struct inode *inode = new_inode_pseudo(s); + struct inode_timespec now;
if (!inode) return ERR_PTR(-ENOMEM); @@ -1069,7 +1093,11 @@ struct inode *alloc_anon_inode(struct super_block *s) inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); inode->i_flags |= S_PRIVATE; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(s); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_mtime, now); + inode_set_inode_time(&inode->i_ctime, now); + return inode; } EXPORT_SYMBOL(alloc_anon_inode); diff --git a/fs/locks.c b/fs/locks.c index d2ee8e3..0f3705e 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1496,7 +1496,7 @@ EXPORT_SYMBOL(__break_lease); * exclusive leases. The justification is that if someone has an * exclusive lease, then they could be modifying it. */ -void lease_get_mtime(struct inode *inode, struct timespec *time) +void lease_get_mtime(struct inode *inode, struct inode_timespec *time) { bool has_lease = false; struct file_lock_context *ctx; @@ -1515,9 +1515,8 @@ void lease_get_mtime(struct inode *inode, struct timespec *time) if (has_lease) *time = current_fs_time(inode->i_sb); else - *time = inode->i_mtime; + *time = inode_get_inode_time(&inode->i_mtime); } - EXPORT_SYMBOL(lease_get_mtime);
/** diff --git a/fs/nsfs.c b/fs/nsfs.c index 8f20d60..a94e1e5 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -51,6 +51,7 @@ void *ns_get_path(struct path *path, struct task_struct *task, struct qstr qname = { .name = "", }; struct dentry *dentry; struct inode *inode; + struct inode_timespec now; struct ns_common *ns; unsigned long d;
@@ -82,7 +83,10 @@ slow: return ERR_PTR(-ENOMEM); } inode->i_ino = ns->inum; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(mnt->mnt_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_ctime, now); + inode_set_inode_time(&inode->i_mtime, now); inode->i_flags |= S_IMMUTABLE; inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &ns_file_operations; diff --git a/fs/pipe.c b/fs/pipe.c index 42cf8dd..ae5dca7 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -637,6 +637,7 @@ static struct inode * get_pipe_inode(void) { struct inode *inode = new_inode_pseudo(pipe_mnt->mnt_sb); struct pipe_inode_info *pipe; + struct inode_timespec now;
if (!inode) goto fail_inode; @@ -662,7 +663,10 @@ static struct inode * get_pipe_inode(void) inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + now = current_fs_time(pipe_mnt->mnt_sb); + inode_set_inode_time(&inode->i_atime, now); + inode_set_inode_time(&inode->i_ctime, now); + inode_set_inode_time(&inode->i_mtime, now);
return inode;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 4adde1e..8edcdac 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -874,7 +874,7 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) acl = NULL; }
- inode->i_ctime = CURRENT_TIME; + inode_set_inode_time(&inode->i_ctime, current_fs_time(inode->i_sb)); set_cached_acl(inode, type, acl); return 0; } diff --git a/fs/stat.c b/fs/stat.c index bc045c7..a789ff2 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -28,9 +28,9 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) stat->gid = inode->i_gid; stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); - stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; + stat->atime = inode_get_inode_time(&inode->i_atime); + stat->mtime = inode_get_inode_time(&inode->i_mtime); + stat->ctime = inode_get_inode_time(&inode->i_ctime); stat->blksize = (1 << inode->i_blkbits); stat->blocks = inode->i_blocks; } diff --git a/fs/utimes.c b/fs/utimes.c index aa138d6..9f62694 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -48,7 +48,7 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; }
-static int utimes_common(struct path *path, struct timespec *times) +static int utimes_common(struct path *path, struct inode_timespec *times) { int error; struct iattr newattrs; @@ -133,7 +133,7 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(int dfd, const char __user *filename, struct timespec *times, +long do_utimes(int dfd, const char __user *filename, struct inode_timespec *times, int flags) { int error = -EINVAL;