// SPDX-License-Identifier: GPL-2.0
/*
 * Test ctime and mtime are updated on truncate(2) and ftruncate(2)
 * Copyright (c) 2013 Red Hat, Inc.  All Rights Reserved.
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define TEST_MSG "this is a test string"

int do_test(const char *file, int is_ftrunc)
{
	int ret;
	int fd;
	struct stat statbuf1;
	struct stat statbuf2;

	ret = 0;
	fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (fd == -1) {
		perror("open(2) failed");
		exit(EXIT_FAILURE);
	}

	ret = write(fd, TEST_MSG, sizeof(TEST_MSG));
	if (ret == -1) {
		perror("write(2) failed");
		exit(EXIT_FAILURE);
	}

	/* Get timestamps before [f]truncate(2) */
	ret = fstat(fd, &statbuf1);
	if (ret == -1) {
		perror("fstat(2) failed");
		exit(EXIT_FAILURE);
	}

	/* Test [f]truncate(2) down */
	sleep(1);
	if (is_ftrunc) {
		ret = ftruncate(fd, 0);
		if (ret == -1) {
			perror("ftruncate(2) down failed");
			exit(EXIT_FAILURE);
		}
	} else {
		ret = truncate(file, 0);
		if (ret == -1) {
			perror("truncate(2) down failed");
			exit(EXIT_FAILURE);
		}
	}

	/* Get timestamps after [f]truncate(2) */
	ret = fstat(fd, &statbuf2);
	if (ret == -1) {
		perror("fstat(2) failed");
		exit(EXIT_FAILURE);
	}

	/* Check whether timestamps got updated on [f]truncate(2) down */
	if (statbuf1.st_ctime == statbuf2.st_ctime) {
		fprintf(stderr, "ctime not updated after %s\n",
			is_ftrunc ? "ftruncate" : "truncate" " down");
		ret++;
	}
	if (statbuf1.st_mtime == statbuf2.st_mtime) {
		fprintf(stderr, "mtime not updated after %s\n",
			is_ftrunc ? "ftruncate" : "truncate" " down");
		ret++;
	}

	/* Test [f]truncate(2) up */
	sleep(1);
	if (is_ftrunc) {
		ret = ftruncate(fd, 123);
		if (ret == -1) {
			perror("ftruncate(2) up failed");
			exit(EXIT_FAILURE);
		}
	} else {
		ret = truncate(file, 123);
		if (ret == -1) {
			perror("truncate(2) up failed");
			exit(EXIT_FAILURE);
		}
	}
	ret = fstat(fd, &statbuf1);
	if (ret == -1) {
		perror("fstat(2) failed");
		exit(EXIT_FAILURE);
	}
	/* Check whether timestamps got updated on [f]truncate(2) up */
	if (statbuf1.st_ctime == statbuf2.st_ctime) {
		fprintf(stderr, "ctime not updated after %s\n",
			is_ftrunc ? "ftruncate" : "truncate" " up");
		ret++;
	}
	if (statbuf1.st_mtime == statbuf2.st_mtime) {
		fprintf(stderr, "mtime not updated after %s\n",
			is_ftrunc ? "ftruncate" : "truncate" " up");
		ret++;
	}

	close(fd);
	return ret;
}

int main(int argc, char *argv[])
{
	int ret;
	char *testfile;

	ret = 0;
	if (argc != 2) {
		fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	testfile = argv[1];

	ret = do_test(testfile, 0);
	ret += do_test(testfile, 1);

	exit(ret);
}
