afs uses 32-bit timestamps everywhere, but mixes signed and unsigned
usage, which is a bit inconsistent. In particular on 32-bit machines,
it currently uses unsigned timestamps (ranging from 1970 to 2106) for
locally modified files, but signed timestamps (rand 1902 to 2038) when
reading from a remote end. On 64-bit machines, we always interpret
timestamps as unsigned here.
This replaces the deprecated time_t with a new explicitly unsigned
afs_time32_t to get a consistent interpretation of inode times
according the the wire protocol definition.
This avoids the y2038 overflow on 32-bit machines, extending the range
to the end of the afs_time32_t in year 2106. On 64-bit machines, using
the shorter type saves a few bytes for each afs_file_status and
afs_volsync saves a few bytes over time_t or time64_t.
Note that mtime_server and struct afs_volsync are not currently
used in any meaningful way and could be removed completely.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
fs/afs/afs.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index b4ff1f7ae4ab..a17f4ce06323 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -26,6 +26,7 @@
typedef unsigned afs_volid_t;
typedef unsigned afs_vnodeid_t;
typedef unsigned long long afs_dataversion_t;
+typedef unsigned afs_time32_t;
typedef enum {
AFSVL_RWVOL, /* read/write volume */
@@ -129,8 +130,8 @@ typedef u32 afs_access_t;
struct afs_file_status {
u64 size; /* file size */
afs_dataversion_t data_version; /* current data version */
- time_t mtime_client; /* last time client changed data */
- time_t mtime_server; /* last time server changed data */
+ afs_time32_t mtime_client; /* last time client changed data */
+ afs_time32_t mtime_server; /* last time server changed data */
unsigned abort_code; /* Abort if bulk-fetching this failed */
afs_file_type_t type; /* file type */
@@ -158,7 +159,7 @@ struct afs_file_status {
* AFS volume synchronisation information
*/
struct afs_volsync {
- time_t creation; /* volume creation time */
+ afs_time32_t creation; /* volume creation time */
};
/*
--
2.9.0
get_seconds() returns an unsigned long can overflow on some architectures
and is deprecated because of that. In cachefs, we cast that number to
a a 32-bit integer, which will overflow in year 2106 on all architectures.
As confirmed by David Howells, the overflow probably isn't harmful
in the end, since the timestamps are only used to make the file names
unique, but they don't strictly have to be in monotonically increasing
order since the files only exist in order to be deleted as quickly
as possible.
Moving to ktime_get_real_seconds() avoids the deprecated interface.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
v2: only change to ktime_get_real_seconds() without printing the
extra leading digits
---
fs/cachefiles/namei.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index ab0bbe93b398..0a38978b8cd3 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -337,7 +337,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
try_again:
/* first step is to make up a grave dentry in the graveyard */
sprintf(nbuffer, "%08x%08x",
- (uint32_t) get_seconds(),
+ (uint32_t) ktime_get_real_seconds(),
(uint32_t) atomic_inc_return(&cache->gravecounter));
/* do the multiway lock magic */
--
2.9.0
get_seconds() returns an unsigned long can overflow on some architectures
and is deprecated because of that. In cachefs, we cast that number to
a a 32-bit integer, which will overflow in year 2106 on all architectures.
The overflow probably isn't harmful in the end, since the timestamps are
only used to make the file names unique, but they don't strictly have to
be in monotonically increasing order. Moving to ktime_get_real_seconds()
avoids the deprecated interface, the question is whether we should still
truncate to 32 bits.
In this patch, I decided to not overflow, but instead to extend the
file names using the 64-bit timestamp, so they will be 17 characters
after 2106 rather than 16.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
fs/cachefiles/namei.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index ab0bbe93b398..5705e29d4506 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -297,7 +297,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
{
struct dentry *grave, *trap;
struct path path, path_to_graveyard;
- char nbuffer[8 + 8 + 1];
+ char nbuffer[16 + 8 + 1];
int ret;
_enter(",'%pd','%pd'", dir, rep);
@@ -336,8 +336,8 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
try_again:
/* first step is to make up a grave dentry in the graveyard */
- sprintf(nbuffer, "%08x%08x",
- (uint32_t) get_seconds(),
+ sprintf(nbuffer, "%08llx%08x",
+ (uint64_t) ktime_get_real_seconds(),
(uint32_t) atomic_inc_return(&cache->gravecounter));
/* do the multiway lock magic */
--
2.9.0
afs uses 32-bit timestamps everywhere, but mixes signed and unsigned
usage, which is a bit inconsistent. In particular on 32-bit machines,
it currently uses unsigned timestamps (ranging from 1970 to 2106) for
locally modified files, but signed timestamps (rand 1902 to 2038) when
reading from a remote end. On 64-bit machines, we always interpret
timestamps as unsigned here.
This replaces the deprecated time_t and get_seconds() interfaces with the
modern time64_t and current_time() to locally store 64-bit timestamps,
taking care to use unsigned interpretation of the raw values everywhere,
which avoids the y2038 overflow and is consistent with the previous
usage on 64-bit machines.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
fs/afs/afs.h | 6 +++---
fs/afs/inode.c | 4 +---
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index b4ff1f7ae4ab..6ca50c293553 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -129,8 +129,8 @@ typedef u32 afs_access_t;
struct afs_file_status {
u64 size; /* file size */
afs_dataversion_t data_version; /* current data version */
- time_t mtime_client; /* last time client changed data */
- time_t mtime_server; /* last time server changed data */
+ time64_t mtime_client; /* last time client changed data */
+ time64_t mtime_server; /* last time server changed data */
unsigned abort_code; /* Abort if bulk-fetching this failed */
afs_file_type_t type; /* file type */
@@ -158,7 +158,7 @@ struct afs_file_status {
* AFS volume synchronisation information
*/
struct afs_volsync {
- time_t creation; /* volume creation time */
+ time64_t creation; /* volume creation time */
};
/*
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 479b7fdda124..0507e52e3330 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -213,9 +213,7 @@ struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
set_nlink(inode, 2);
inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID;
- inode->i_ctime.tv_sec = get_seconds();
- inode->i_ctime.tv_nsec = 0;
- inode->i_atime = inode->i_mtime = inode->i_ctime;
+ inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
inode->i_blocks = 0;
inode_set_iversion_raw(inode, 0);
inode->i_generation = 0;
--
2.9.0
get_seconds() is deprecated in favor of ktime_get_real_seconds(),
which returns a 64-bit timestamp.
In the SYSV file system, the superblock timestamp is only 32 bits
wide, and it is used to check whether a file system is clean, so
the best solution seems to be to force a wraparound and explicitly
convert it to an unsigned 32-bit value.
This is independent of the inode timestamps that are also 32-bit
wide on disk and that come from current_time().
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
fs/sysv/inode.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index bec9f79adb25..499a20a5a010 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -35,7 +35,7 @@
static int sysv_sync_fs(struct super_block *sb, int wait)
{
struct sysv_sb_info *sbi = SYSV_SB(sb);
- unsigned long time = get_seconds(), old_time;
+ u32 time = (u32)ktime_get_real_seconds(), old_time;
mutex_lock(&sbi->s_lock);
@@ -46,8 +46,8 @@ static int sysv_sync_fs(struct super_block *sb, int wait)
*/
old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
if (sbi->s_type == FSTYPE_SYSV4) {
- if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
- *sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38 - time);
+ if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38u - old_time))
+ *sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38u - time);
*sbi->s_sb_time = cpu_to_fs32(sbi, time);
mark_buffer_dirty(sbi->s_bh2);
}
--
2.9.0
get_seconds() is deprecated because of the 32-bit overflow and will
be removed. All callers in ufs also truncate to a 32-bit number, so
nothing changes during the conversion, but this should be harmless as the
superblock and cylinder group timestamps are not visible to user space,
except for checking the fs-dirty state, wich works fine across the
overflow.
This moves the call to get_seconds() into a new inline function, with
a comment explaining the constraints, while converting it to
ktime_get_real_seconds().
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
fs/ufs/balloc.c | 4 ++--
fs/ufs/ialloc.c | 2 +-
fs/ufs/super.c | 4 ++--
fs/ufs/util.h | 14 ++++++++++++++
4 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index e727ee07dbe4..075d3d9114c8 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -547,7 +547,7 @@ static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
/*
* Block can be extended
*/
- ucg->cg_time = cpu_to_fs32(sb, get_seconds());
+ ucg->cg_time = ufs_get_seconds(sb);
for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
break;
@@ -639,7 +639,7 @@ static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
if (!ufs_cg_chkmagic(sb, ucg))
ufs_panic (sb, "ufs_alloc_fragments",
"internal error, bad magic number on cg %u", cgno);
- ucg->cg_time = cpu_to_fs32(sb, get_seconds());
+ ucg->cg_time = ufs_get_seconds(sb);
if (count == uspi->s_fpb) {
result = ufs_alloccg_block (inode, ucpi, goal, err);
diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c
index e1ef0f0a1353..c678fff2a04d 100644
--- a/fs/ufs/ialloc.c
+++ b/fs/ufs/ialloc.c
@@ -89,7 +89,7 @@ void ufs_free_inode (struct inode * inode)
if (!ufs_cg_chkmagic(sb, ucg))
ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number");
- ucg->cg_time = cpu_to_fs32(sb, get_seconds());
+ ucg->cg_time = ufs_get_seconds(sb);
is_directory = S_ISDIR(inode->i_mode);
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 488088141451..a4e07e910f1b 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -698,7 +698,7 @@ static int ufs_sync_fs(struct super_block *sb, int wait)
usb1 = ubh_get_usb_first(uspi);
usb3 = ubh_get_usb_third(uspi);
- usb1->fs_time = cpu_to_fs32(sb, get_seconds());
+ usb1->fs_time = ufs_get_seconds(sb);
if ((flags & UFS_ST_MASK) == UFS_ST_SUN ||
(flags & UFS_ST_MASK) == UFS_ST_SUNOS ||
(flags & UFS_ST_MASK) == UFS_ST_SUNx86)
@@ -1342,7 +1342,7 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
*/
if (*mount_flags & SB_RDONLY) {
ufs_put_super_internal(sb);
- usb1->fs_time = cpu_to_fs32(sb, get_seconds());
+ usb1->fs_time = ufs_get_seconds(sb);
if ((flags & UFS_ST_MASK) == UFS_ST_SUN
|| (flags & UFS_ST_MASK) == UFS_ST_SUNOS
|| (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
index 1907be6d5808..1fd3011ea623 100644
--- a/fs/ufs/util.h
+++ b/fs/ufs/util.h
@@ -590,3 +590,17 @@ static inline int ufs_is_data_ptr_zero(struct ufs_sb_private_info *uspi,
else
return *(__fs32 *)p == 0;
}
+
+static inline __fs32 ufs_get_seconds(struct super_block *sbp)
+{
+ time64_t now = ktime_get_real_seconds();
+
+ /* Signed 32-bit interpretation wraps around in 2038, which
+ * happens in ufs1 inode stamps but not ufs2 using 64-bits
+ * stamps. For superblock and blockgroup, let's assume
+ * unsigned 32-bit stamps, which are good until y2106.
+ * Wrap around rather than clamp here to make the dirty
+ * file system detection work in the superblock stamp.
+ */
+ return cpu_to_fs32(sbp, lower_32_bits(now));
+}
--
2.9.0