diff mbox series

jffs2: Fix truncate never update m/ctime

Message ID 1637077036-67090-1-git-send-email-zhongjubin@huawei.com
State New
Delegated to: Richard Weinberger
Headers show
Series jffs2: Fix truncate never update m/ctime | expand

Commit Message

Jubin Zhong Nov. 16, 2021, 3:37 p.m. UTC
From: zhongjubin <zhongjubin@huawei.com>

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@huawei.com>

----
v1:
  https://lore.kernel.org/all/1636974018-31285-1-git-send-email-zhongjubin@huawei.com/
  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 mbox series

Patch

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);