[RFC 04/15] vfs: Add support for vfs code to use 64 bit time

Deepa Dinamani
2016-01-07T05:37:02Z

VFS currently uses struct timespec timestamps which are not
y2038 safe.

Change all the struct inode timestamps accesses through
accessor macros only. This will help the switch over
to 64 bit times seamlessly.

Use struct inode_timespec aliases everywhere.
This will change timestamp data types to 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 latter supports range checking on timestamps.

Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
---
fs/attr.c | 15 +++++++-------
fs/bad_inode.c | 10 +++++++--
fs/binfmt_misc.c | 7 +++++--
fs/inode.c | 53 ++++++++++++++++++++++++++++++++----------------
fs/libfs.c | 45 ++++++++++++++++++++++++++++++++--------
fs/locks.c | 5 ++---
fs/nsfs.c | 6 +++++-
fs/pipe.c | 6 +++++-
fs/posix_acl.c | 2 +-
fs/stack.c | 6 +++---
fs/stat.c | 6 +++---
fs/utimes.c | 6 ++++--
include/linux/fs_stack.h | 9 ++++----
13 files changed, 121 insertions(+), 55 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 6530ced..4156239 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);
+ VFS_INODE_SET_XTIME(i_atime, inode,
+ 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);
+ VFS_INODE_SET_XTIME(i_mtime, inode,
+ 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);
+ VFS_INODE_SET_XTIME(i_ctime, inode,
+ 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));
@@ -210,7 +210,6 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
}

now = current_fs_time(inode->i_sb);
-
attr->ia_ctime = now;
if (!(ia_valid & ATTR_ATIME_SET))
attr->ia_atime = now;
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 103f5d7..3c51e22 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -169,11 +169,17 @@ 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);
+
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, 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..4fd4437 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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
}
return inode;
}
diff --git a/fs/inode.c b/fs/inode.c
index 4c8f719..d3d64dc 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 = VFS_INODE_GET_XTIME(i_atime, inode);
+ ctime = VFS_INODE_GET_XTIME(i_ctime, inode);
+ mtime = VFS_INODE_GET_XTIME(i_mtime, inode);
+
/*
* 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,19 @@ 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;
+ VFS_INODE_SET_XTIME(i_atime, inode, *time);
if (flags & S_VERSION)
inode_inc_iversion(inode);
if (flags & S_CTIME)
- inode->i_ctime = *time;
+ VFS_INODE_SET_XTIME(i_ctime, inode, *time);
if (flags & S_MTIME)
- inode->i_mtime = *time;
+ VFS_INODE_SET_XTIME(i_mtime, inode, *time);

if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION))
iflags |= I_DIRTY_SYNC;
@@ -1584,9 +1594,10 @@ 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 +1617,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;
@@ -1621,11 +1633,11 @@ bool atime_needs_update(const struct path *path, struct inode *inode)
return false;

now = current_fs_time(inode->i_sb);
-
if (!relatime_need_update(mnt, inode, now))
return false;

- if (timespec_equal(&inode->i_atime, &now))
+ atime = VFS_INODE_GET_XTIME(i_atime, inode);
+ if (inode_timespec_equal(&atime, &now))
return false;

return true;
@@ -1635,7 +1647,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 +1782,10 @@ 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;
+ struct inode_timespec mtime;
+ struct inode_timespec ctime;
+
int sync_it = 0;
int ret;

@@ -1779,10 +1794,14 @@ int file_update_time(struct file *file)
return 0;

now = current_fs_time(inode->i_sb);
- if (!timespec_equal(&inode->i_mtime, &now))
+
+ mtime = VFS_INODE_GET_XTIME(i_mtime, inode);
+ ctime = VFS_INODE_GET_XTIME(i_ctime, inode);
+
+ if (!inode_timespec_equal(&mtime, &now))
sync_it = S_MTIME;

- if (!timespec_equal(&inode->i_ctime, &now))
+ if (!inode_timespec_equal(&ctime, &now))
sync_it |= S_CTIME;

if (IS_I_VERSION(inode))
diff --git a/fs/libfs.c b/fs/libfs.c
index 4fa2002..5a0c7c2 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))
@@ -240,7 +241,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);
+ VFS_INODE_SET_XTIME(i_atime, root, now);
+ VFS_INODE_SET_XTIME(i_ctime, root, now);
+ VFS_INODE_SET_XTIME(i_mtime, root, now);
+
dentry = __d_alloc(s, &d_name);
if (!dentry) {
iput(root);
@@ -269,8 +274,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;
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, dir, now);
+ VFS_INODE_SET_XTIME(i_ctime, dir, now);
inc_nlink(inode);
ihold(inode);
dget(dentry);
@@ -303,8 +311,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;
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, dir, now);
+ VFS_INODE_SET_XTIME(i_ctime, dir, now);
drop_nlink(inode);
dput(dentry);
return 0;
@@ -328,6 +339,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;
@@ -343,8 +355,13 @@ 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);
+
+ VFS_INODE_SET_XTIME(i_ctime, old_dir, now);
+ VFS_INODE_SET_XTIME(i_mtime, old_dir, now);
+ VFS_INODE_SET_XTIME(i_ctime, new_dir, now);
+ VFS_INODE_SET_XTIME(i_mtime, new_dir, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);

return 0;
}
@@ -478,6 +495,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;
@@ -497,7 +515,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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
set_nlink(inode, 2);
@@ -523,7 +544,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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
inode->i_fop = files->ops;
inode->i_ino = i;
d_add(dentry, inode);
@@ -1056,6 +1080,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);
@@ -1074,7 +1099,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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+
return inode;
}
EXPORT_SYMBOL(alloc_anon_inode);
diff --git a/fs/locks.c b/fs/locks.c
index 15e2b60..2b818eb 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1491,7 +1491,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;
@@ -1510,9 +1510,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 = VFS_INODE_GET_XTIME(i_mtime, inode);
}
-
EXPORT_SYMBOL(lease_get_mtime);

/**
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 8f20d60..a079fc9 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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, 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..5d414a3 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);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);

return inode;

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 711dd51..0ac55c9 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -859,7 +859,7 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
acl = NULL;
}

- inode->i_ctime = CURRENT_TIME;
+ VFS_INODE_SET_XTIME(i_ctime, inode, current_fs_time(inode->i_sb));
set_cached_acl(inode, type, acl);
return 0;
}
diff --git a/fs/stack.c b/fs/stack.c
index a54e33e..812bbbb 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -66,9 +66,9 @@ void fsstack_copy_attr_all(struct inode *dest, const struct inode *src)
dest->i_uid = src->i_uid;
dest->i_gid = src->i_gid;
dest->i_rdev = src->i_rdev;
- dest->i_atime = src->i_atime;
- dest->i_mtime = src->i_mtime;
- dest->i_ctime = src->i_ctime;
+ VFS_INODE_SET_XTIME(i_atime, dest, VFS_INODE_GET_XTIME(i_atime, src));
+ VFS_INODE_SET_XTIME(i_mtime, dest, VFS_INODE_GET_XTIME(i_mtime, src));
+ VFS_INODE_SET_XTIME(i_ctime, dest, VFS_INODE_GET_XTIME(i_ctime, src));
dest->i_blkbits = src->i_blkbits;
dest->i_flags = src->i_flags;
set_nlink(dest, src->i_nlink);
diff --git a/fs/stat.c b/fs/stat.c
index bc045c7..c448313 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 = VFS_INODE_GET_XTIME(i_atime, inode);
+ stat->mtime = VFS_INODE_GET_XTIME(i_mtime, inode);
+ stat->ctime = VFS_INODE_GET_XTIME(i_ctime, inode);
stat->blksize = (1 << inode->i_blkbits);
stat->blocks = inode->i_blocks;
}
diff --git a/fs/utimes.c b/fs/utimes.c
index aa138d6..c23c8e6 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -48,11 +48,12 @@ 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;
struct inode *inode = path->dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
struct inode *delegated_inode = NULL;

error = mnt_want_write(path->mnt);
@@ -133,7 +134,8 @@ 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;
diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h
index da317c7..2d2bb50 100644
--- a/include/linux/fs_stack.h
+++ b/include/linux/fs_stack.h
@@ -15,15 +15,16 @@ extern void fsstack_copy_inode_size(struct inode *dst, struct inode *src);
static inline void fsstack_copy_attr_atime(struct inode *dest,
const struct inode *src)
{
- dest->i_atime = src->i_atime;
+ VFS_INODE_SET_XTIME(i_atime, dest, VFS_INODE_GET_XTIME(i_atime, src));
}

static inline void fsstack_copy_attr_times(struct inode *dest,
const struct inode *src)
{
- dest->i_atime = src->i_atime;
- dest->i_mtime = src->i_mtime;
- dest->i_ctime = src->i_ctime;
+ VFS_INODE_SET_XTIME(i_atime, dest, VFS_INODE_GET_XTIME(i_atime, src));
+ VFS_INODE_SET_XTIME(i_mtime, dest, VFS_INODE_GET_XTIME(i_mtime, src));
+ VFS_INODE_SET_XTIME(i_ctime, dest, VFS_INODE_GET_XTIME(i_ctime, src));
+
}

#endif /* _LINUX_FS_STACK_H */
--
1.9.1