[PATCH] jffs2: Fix truncate never update m/ctime

From: Jubin Zhong
Date: Tue Nov 16 2021 - 10:37:27 EST


From: zhongjubin <zhongjubin@xxxxxxxxxx>

Syscall truncate() never updates m/ctime in jffs2 even if the file
size is changed. This is incorrect according to man file:

truncate (2):
If the size changed, then the st_ctime and st_mtime fields
(respectively, time of last status change and time of last
modification; see stat(2)) for the file are updated, and the
set-user-ID and set-group-ID mode bits may be cleared.

This is because truncate() will not set ATTR_CTIME and ATTR_MTIME even
when file size is changed. Fix this special case just like what other
filesystems do.

Test Steps:
1. cd /path/to/mnt/point
2. dd if=/dev/zero of=test bs=1M count=1
3. stat test
4. /bin/my_truncate -s 1024 test
5. stat test
6. compare m/ctime of step 5 with step 3

Program source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
int ret;
char file_name[128] = {0};

if (argc < 4 || argv == NULL || argv[1] == NULL ||
argv[2] == NULL || argv[3] == NULL) {
return -1;
}

if (strcmp(argv[1], "-s")) {
return -1;
}

if (realpath(argv[3], file_name) == NULL) {
printf("truncate: input file name %s err.\n", argv[3]);
return -1;
}

off_t size = (off_t)strtol(argv[2], 0, 0);
ret = truncate(file_name, size);
if (ret) {
printf("truncate return err %d\n", ret);
}
return ret;
}

Signed-off-by: zhongjubin <zhongjubin@xxxxxxxxxx>

----
v1:
https://lore.kernel.org/all/1636974018-31285-1-git-send-email-zhongjubin@xxxxxxxxxx/
I considered fixing this problem in vfs_truncate(), but it is
suggested to implement the proper semantics in jffs's ->setattr.
---
fs/jffs2/fs.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 2ac410477c4f..181f053d072b 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -114,8 +114,18 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)

ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
- ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
- ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
+ /*
+ * Special case for truncate() where we should update the times despite not having
+ * ATTR_CTIME and ATTR_MTIME set.
+ */
+ if ((ivalid & ATTR_SIZE) && (iattr->ia_size != inode->i_size) &&
+ !(ivalid & (ATTR_CTIME | ATTR_MTIME))) {
+ ri->mtime = cpu_to_je32(I_SEC(iattr->ia_mtime));
+ ri->ctime = cpu_to_je32(I_SEC(iattr->ia_ctime));
+ } else {
+ ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
+ ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
+ }

ri->offset = cpu_to_je32(0);
ri->csize = ri->dsize = cpu_to_je32(mdatalen);
--
2.12.3