summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-09-16 06:44:20 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2016-09-16 06:44:20 -0400
commit598e3c8f72f5b77c84d2cb26cfd936ffb3cfdbaa (patch)
tree485f1dd152279acde134219e011f3f1dadbe5495
parentf2b20f6ee842313a0d681dbbf7f87b70291a6a3b (diff)
vfs: update ovl inode before relatime check
On overlayfs relatime_need_update() needs inode times to be correct on overlay inode. But i_mtime and i_ctime are updated by filesystem code on underlying inode only, so they will be out-of-date on the overlay inode. This patch copies the times from the underlying inode if needed. This can't be done if called from RCU lookup (link following) but link m/ctime are not updated by fs, so this is all right. This patch doesn't change functionality for anything but overlayfs. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/inode.c33
-rw-r--r--fs/internal.h9
-rw-r--r--fs/namei.c2
-rw-r--r--include/linux/fs.h1
4 files changed, 37 insertions, 8 deletions
diff --git a/fs/inode.c b/fs/inode.c
index 7e3ef3af3db9..4a1fc1631e00 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1536,16 +1536,36 @@ sector_t bmap(struct inode *inode, sector_t block)
1536EXPORT_SYMBOL(bmap); 1536EXPORT_SYMBOL(bmap);
1537 1537
1538/* 1538/*
1539 * Update times in overlayed inode from underlying real inode
1540 */
1541static void update_ovl_inode_times(struct dentry *dentry, struct inode *inode,
1542 bool rcu)
1543{
1544 if (!rcu) {
1545 struct inode *realinode = d_real_inode(dentry);
1546
1547 if (unlikely(inode != realinode) &&
1548 (!timespec_equal(&inode->i_mtime, &realinode->i_mtime) ||
1549 !timespec_equal(&inode->i_ctime, &realinode->i_ctime))) {
1550 inode->i_mtime = realinode->i_mtime;
1551 inode->i_ctime = realinode->i_ctime;
1552 }
1553 }
1554}
1555
1556/*
1539 * With relative atime, only update atime if the previous atime is 1557 * With relative atime, only update atime if the previous atime is
1540 * earlier than either the ctime or mtime or if at least a day has 1558 * earlier than either the ctime or mtime or if at least a day has
1541 * passed since the last atime update. 1559 * passed since the last atime update.
1542 */ 1560 */
1543static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, 1561static int relatime_need_update(const struct path *path, struct inode *inode,
1544 struct timespec now) 1562 struct timespec now, bool rcu)
1545{ 1563{
1546 1564
1547 if (!(mnt->mnt_flags & MNT_RELATIME)) 1565 if (!(path->mnt->mnt_flags & MNT_RELATIME))
1548 return 1; 1566 return 1;
1567
1568 update_ovl_inode_times(path->dentry, inode, rcu);
1549 /* 1569 /*
1550 * Is mtime younger than atime? If yes, update atime: 1570 * Is mtime younger than atime? If yes, update atime:
1551 */ 1571 */
@@ -1612,7 +1632,8 @@ static int update_time(struct inode *inode, struct timespec *time, int flags)
1612 * This function automatically handles read only file systems and media, 1632 * This function automatically handles read only file systems and media,
1613 * as well as the "noatime" flag and inode specific "noatime" markers. 1633 * as well as the "noatime" flag and inode specific "noatime" markers.
1614 */ 1634 */
1615bool atime_needs_update(const struct path *path, struct inode *inode) 1635bool __atime_needs_update(const struct path *path, struct inode *inode,
1636 bool rcu)
1616{ 1637{
1617 struct vfsmount *mnt = path->mnt; 1638 struct vfsmount *mnt = path->mnt;
1618 struct timespec now; 1639 struct timespec now;
@@ -1638,7 +1659,7 @@ bool atime_needs_update(const struct path *path, struct inode *inode)
1638 1659
1639 now = current_fs_time(inode->i_sb); 1660 now = current_fs_time(inode->i_sb);
1640 1661
1641 if (!relatime_need_update(mnt, inode, now)) 1662 if (!relatime_need_update(path, inode, now, rcu))
1642 return false; 1663 return false;
1643 1664
1644 if (timespec_equal(&inode->i_atime, &now)) 1665 if (timespec_equal(&inode->i_atime, &now))
@@ -1653,7 +1674,7 @@ void touch_atime(const struct path *path)
1653 struct inode *inode = d_inode(path->dentry); 1674 struct inode *inode = d_inode(path->dentry);
1654 struct timespec now; 1675 struct timespec now;
1655 1676
1656 if (!atime_needs_update(path, inode)) 1677 if (!__atime_needs_update(path, inode, false))
1657 return; 1678 return;
1658 1679
1659 if (!sb_start_write_trylock(inode->i_sb)) 1680 if (!sb_start_write_trylock(inode->i_sb))
diff --git a/fs/internal.h b/fs/internal.h
index ba0737649d4a..a63da5e96148 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -120,6 +120,15 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
120extern void inode_add_lru(struct inode *inode); 120extern void inode_add_lru(struct inode *inode);
121extern int dentry_needs_remove_privs(struct dentry *dentry); 121extern int dentry_needs_remove_privs(struct dentry *dentry);
122 122
123extern bool __atime_needs_update(const struct path *, struct inode *, bool);
124static inline bool atime_needs_update_rcu(const struct path *path,
125 struct inode *inode)
126{
127 return __atime_needs_update(path, inode, true);
128}
129
130extern bool atime_needs_update_rcu(const struct path *, struct inode *);
131
123/* 132/*
124 * fs-writeback.c 133 * fs-writeback.c
125 */ 134 */
diff --git a/fs/namei.c b/fs/namei.c
index adb04146df09..4bbcae1ba58e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1015,7 +1015,7 @@ const char *get_link(struct nameidata *nd)
1015 if (!(nd->flags & LOOKUP_RCU)) { 1015 if (!(nd->flags & LOOKUP_RCU)) {
1016 touch_atime(&last->link); 1016 touch_atime(&last->link);
1017 cond_resched(); 1017 cond_resched();
1018 } else if (atime_needs_update(&last->link, inode)) { 1018 } else if (atime_needs_update_rcu(&last->link, inode)) {
1019 if (unlikely(unlazy_walk(nd, NULL, 0))) 1019 if (unlikely(unlazy_walk(nd, NULL, 0)))
1020 return ERR_PTR(-ECHILD); 1020 return ERR_PTR(-ECHILD);
1021 touch_atime(&last->link); 1021 touch_atime(&last->link);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7c391366fb43..7db097d673a8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2007,7 +2007,6 @@ enum file_time_flags {
2007 S_VERSION = 8, 2007 S_VERSION = 8,
2008}; 2008};
2009 2009
2010extern bool atime_needs_update(const struct path *, struct inode *);
2011extern void touch_atime(const struct path *); 2010extern void touch_atime(const struct path *);
2012static inline void file_accessed(struct file *file) 2011static inline void file_accessed(struct file *file)
2013{ 2012{