162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Test the statx() system call.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Note that the output of this program is intended to look like the output of
562306a36Sopenharmony_ci * /bin/stat where possible.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved.
862306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define _GNU_SOURCE
1262306a36Sopenharmony_ci#define _ATFILE_SOURCE
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci#include <unistd.h>
1762306a36Sopenharmony_ci#include <ctype.h>
1862306a36Sopenharmony_ci#include <errno.h>
1962306a36Sopenharmony_ci#include <time.h>
2062306a36Sopenharmony_ci#include <sys/syscall.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci#include <linux/stat.h>
2362306a36Sopenharmony_ci#include <linux/fcntl.h>
2462306a36Sopenharmony_ci#define statx foo
2562306a36Sopenharmony_ci#define statx_timestamp foo_timestamp
2662306a36Sopenharmony_cistruct statx;
2762306a36Sopenharmony_cistruct statx_timestamp;
2862306a36Sopenharmony_ci#include <sys/stat.h>
2962306a36Sopenharmony_ci#undef statx
3062306a36Sopenharmony_ci#undef statx_timestamp
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define AT_STATX_SYNC_TYPE	0x6000
3362306a36Sopenharmony_ci#define AT_STATX_SYNC_AS_STAT	0x0000
3462306a36Sopenharmony_ci#define AT_STATX_FORCE_SYNC	0x2000
3562306a36Sopenharmony_ci#define AT_STATX_DONT_SYNC	0x4000
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifndef __NR_statx
3862306a36Sopenharmony_ci#define __NR_statx -1
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic __attribute__((unused))
4262306a36Sopenharmony_cissize_t statx(int dfd, const char *filename, unsigned flags,
4362306a36Sopenharmony_ci	      unsigned int mask, struct statx *buffer)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void print_time(const char *field, struct statx_timestamp *ts)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct tm tm;
5162306a36Sopenharmony_ci	time_t tim;
5262306a36Sopenharmony_ci	char buffer[100];
5362306a36Sopenharmony_ci	int len;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	tim = ts->tv_sec;
5662306a36Sopenharmony_ci	if (!localtime_r(&tim, &tm)) {
5762306a36Sopenharmony_ci		perror("localtime_r");
5862306a36Sopenharmony_ci		exit(1);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	len = strftime(buffer, 100, "%F %T", &tm);
6162306a36Sopenharmony_ci	if (len == 0) {
6262306a36Sopenharmony_ci		perror("strftime");
6362306a36Sopenharmony_ci		exit(1);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	printf("%s", field);
6662306a36Sopenharmony_ci	fwrite(buffer, 1, len, stdout);
6762306a36Sopenharmony_ci	printf(".%09u", ts->tv_nsec);
6862306a36Sopenharmony_ci	len = strftime(buffer, 100, "%z", &tm);
6962306a36Sopenharmony_ci	if (len == 0) {
7062306a36Sopenharmony_ci		perror("strftime2");
7162306a36Sopenharmony_ci		exit(1);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	fwrite(buffer, 1, len, stdout);
7462306a36Sopenharmony_ci	printf("\n");
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void dump_statx(struct statx *stx)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	char buffer[256], ft = '?';
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	printf("results=%x\n", stx->stx_mask);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	printf(" ");
8462306a36Sopenharmony_ci	if (stx->stx_mask & STATX_SIZE)
8562306a36Sopenharmony_ci		printf(" Size: %-15llu", (unsigned long long)stx->stx_size);
8662306a36Sopenharmony_ci	if (stx->stx_mask & STATX_BLOCKS)
8762306a36Sopenharmony_ci		printf(" Blocks: %-10llu", (unsigned long long)stx->stx_blocks);
8862306a36Sopenharmony_ci	printf(" IO Block: %-6llu", (unsigned long long)stx->stx_blksize);
8962306a36Sopenharmony_ci	if (stx->stx_mask & STATX_TYPE) {
9062306a36Sopenharmony_ci		switch (stx->stx_mode & S_IFMT) {
9162306a36Sopenharmony_ci		case S_IFIFO:	printf("  FIFO\n");			ft = 'p'; break;
9262306a36Sopenharmony_ci		case S_IFCHR:	printf("  character special file\n");	ft = 'c'; break;
9362306a36Sopenharmony_ci		case S_IFDIR:	printf("  directory\n");		ft = 'd'; break;
9462306a36Sopenharmony_ci		case S_IFBLK:	printf("  block special file\n");	ft = 'b'; break;
9562306a36Sopenharmony_ci		case S_IFREG:	printf("  regular file\n");		ft = '-'; break;
9662306a36Sopenharmony_ci		case S_IFLNK:	printf("  symbolic link\n");		ft = 'l'; break;
9762306a36Sopenharmony_ci		case S_IFSOCK:	printf("  socket\n");			ft = 's'; break;
9862306a36Sopenharmony_ci		default:
9962306a36Sopenharmony_ci			printf(" unknown type (%o)\n", stx->stx_mode & S_IFMT);
10062306a36Sopenharmony_ci			break;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		printf(" no type\n");
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	sprintf(buffer, "%02x:%02x", stx->stx_dev_major, stx->stx_dev_minor);
10762306a36Sopenharmony_ci	printf("Device: %-15s", buffer);
10862306a36Sopenharmony_ci	if (stx->stx_mask & STATX_INO)
10962306a36Sopenharmony_ci		printf(" Inode: %-11llu", (unsigned long long) stx->stx_ino);
11062306a36Sopenharmony_ci	if (stx->stx_mask & STATX_NLINK)
11162306a36Sopenharmony_ci		printf(" Links: %-5u", stx->stx_nlink);
11262306a36Sopenharmony_ci	if (stx->stx_mask & STATX_TYPE) {
11362306a36Sopenharmony_ci		switch (stx->stx_mode & S_IFMT) {
11462306a36Sopenharmony_ci		case S_IFBLK:
11562306a36Sopenharmony_ci		case S_IFCHR:
11662306a36Sopenharmony_ci			printf(" Device type: %u,%u",
11762306a36Sopenharmony_ci			       stx->stx_rdev_major, stx->stx_rdev_minor);
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	printf("\n");
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (stx->stx_mask & STATX_MODE)
12462306a36Sopenharmony_ci		printf("Access: (%04o/%c%c%c%c%c%c%c%c%c%c)  ",
12562306a36Sopenharmony_ci		       stx->stx_mode & 07777,
12662306a36Sopenharmony_ci		       ft,
12762306a36Sopenharmony_ci		       stx->stx_mode & S_IRUSR ? 'r' : '-',
12862306a36Sopenharmony_ci		       stx->stx_mode & S_IWUSR ? 'w' : '-',
12962306a36Sopenharmony_ci		       stx->stx_mode & S_IXUSR ? 'x' : '-',
13062306a36Sopenharmony_ci		       stx->stx_mode & S_IRGRP ? 'r' : '-',
13162306a36Sopenharmony_ci		       stx->stx_mode & S_IWGRP ? 'w' : '-',
13262306a36Sopenharmony_ci		       stx->stx_mode & S_IXGRP ? 'x' : '-',
13362306a36Sopenharmony_ci		       stx->stx_mode & S_IROTH ? 'r' : '-',
13462306a36Sopenharmony_ci		       stx->stx_mode & S_IWOTH ? 'w' : '-',
13562306a36Sopenharmony_ci		       stx->stx_mode & S_IXOTH ? 'x' : '-');
13662306a36Sopenharmony_ci	if (stx->stx_mask & STATX_UID)
13762306a36Sopenharmony_ci		printf("Uid: %5d   ", stx->stx_uid);
13862306a36Sopenharmony_ci	if (stx->stx_mask & STATX_GID)
13962306a36Sopenharmony_ci		printf("Gid: %5d\n", stx->stx_gid);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (stx->stx_mask & STATX_ATIME)
14262306a36Sopenharmony_ci		print_time("Access: ", &stx->stx_atime);
14362306a36Sopenharmony_ci	if (stx->stx_mask & STATX_MTIME)
14462306a36Sopenharmony_ci		print_time("Modify: ", &stx->stx_mtime);
14562306a36Sopenharmony_ci	if (stx->stx_mask & STATX_CTIME)
14662306a36Sopenharmony_ci		print_time("Change: ", &stx->stx_ctime);
14762306a36Sopenharmony_ci	if (stx->stx_mask & STATX_BTIME)
14862306a36Sopenharmony_ci		print_time(" Birth: ", &stx->stx_btime);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (stx->stx_attributes_mask) {
15162306a36Sopenharmony_ci		unsigned char bits, mbits;
15262306a36Sopenharmony_ci		int loop, byte;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		static char attr_representation[64 + 1] =
15562306a36Sopenharmony_ci			/* STATX_ATTR_ flags: */
15662306a36Sopenharmony_ci			"????????"	/* 63-56 */
15762306a36Sopenharmony_ci			"????????"	/* 55-48 */
15862306a36Sopenharmony_ci			"????????"	/* 47-40 */
15962306a36Sopenharmony_ci			"????????"	/* 39-32 */
16062306a36Sopenharmony_ci			"????????"	/* 31-24	0x00000000-ff000000 */
16162306a36Sopenharmony_ci			"????????"	/* 23-16	0x00000000-00ff0000 */
16262306a36Sopenharmony_ci			"???me???"	/* 15- 8	0x00000000-0000ff00 */
16362306a36Sopenharmony_ci			"?dai?c??"	/*  7- 0	0x00000000-000000ff */
16462306a36Sopenharmony_ci			;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		printf("Attributes: %016llx (",
16762306a36Sopenharmony_ci		       (unsigned long long)stx->stx_attributes);
16862306a36Sopenharmony_ci		for (byte = 64 - 8; byte >= 0; byte -= 8) {
16962306a36Sopenharmony_ci			bits = stx->stx_attributes >> byte;
17062306a36Sopenharmony_ci			mbits = stx->stx_attributes_mask >> byte;
17162306a36Sopenharmony_ci			for (loop = 7; loop >= 0; loop--) {
17262306a36Sopenharmony_ci				int bit = byte + loop;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci				if (!(mbits & 0x80))
17562306a36Sopenharmony_ci					putchar('.');	/* Not supported */
17662306a36Sopenharmony_ci				else if (bits & 0x80)
17762306a36Sopenharmony_ci					putchar(attr_representation[63 - bit]);
17862306a36Sopenharmony_ci				else
17962306a36Sopenharmony_ci					putchar('-');	/* Not set */
18062306a36Sopenharmony_ci				bits <<= 1;
18162306a36Sopenharmony_ci				mbits <<= 1;
18262306a36Sopenharmony_ci			}
18362306a36Sopenharmony_ci			if (byte)
18462306a36Sopenharmony_ci				putchar(' ');
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		printf(")\n");
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void dump_hex(unsigned long long *data, int from, int to)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	unsigned offset, print_offset = 1, col = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	from /= 8;
19562306a36Sopenharmony_ci	to = (to + 7) / 8;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	for (offset = from; offset < to; offset++) {
19862306a36Sopenharmony_ci		if (print_offset) {
19962306a36Sopenharmony_ci			printf("%04x: ", offset * 8);
20062306a36Sopenharmony_ci			print_offset = 0;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci		printf("%016llx", data[offset]);
20362306a36Sopenharmony_ci		col++;
20462306a36Sopenharmony_ci		if ((col & 3) == 0) {
20562306a36Sopenharmony_ci			printf("\n");
20662306a36Sopenharmony_ci			print_offset = 1;
20762306a36Sopenharmony_ci		} else {
20862306a36Sopenharmony_ci			printf(" ");
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!print_offset)
21362306a36Sopenharmony_ci		printf("\n");
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciint main(int argc, char **argv)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct statx stx;
21962306a36Sopenharmony_ci	int ret, raw = 0, atflag = AT_SYMLINK_NOFOLLOW;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	unsigned int mask = STATX_BASIC_STATS | STATX_BTIME;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	for (argv++; *argv; argv++) {
22462306a36Sopenharmony_ci		if (strcmp(*argv, "-F") == 0) {
22562306a36Sopenharmony_ci			atflag &= ~AT_STATX_SYNC_TYPE;
22662306a36Sopenharmony_ci			atflag |= AT_STATX_FORCE_SYNC;
22762306a36Sopenharmony_ci			continue;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci		if (strcmp(*argv, "-D") == 0) {
23062306a36Sopenharmony_ci			atflag &= ~AT_STATX_SYNC_TYPE;
23162306a36Sopenharmony_ci			atflag |= AT_STATX_DONT_SYNC;
23262306a36Sopenharmony_ci			continue;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci		if (strcmp(*argv, "-L") == 0) {
23562306a36Sopenharmony_ci			atflag &= ~AT_SYMLINK_NOFOLLOW;
23662306a36Sopenharmony_ci			continue;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci		if (strcmp(*argv, "-O") == 0) {
23962306a36Sopenharmony_ci			mask &= ~STATX_BASIC_STATS;
24062306a36Sopenharmony_ci			continue;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci		if (strcmp(*argv, "-A") == 0) {
24362306a36Sopenharmony_ci			atflag |= AT_NO_AUTOMOUNT;
24462306a36Sopenharmony_ci			continue;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci		if (strcmp(*argv, "-R") == 0) {
24762306a36Sopenharmony_ci			raw = 1;
24862306a36Sopenharmony_ci			continue;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		memset(&stx, 0xbf, sizeof(stx));
25262306a36Sopenharmony_ci		ret = statx(AT_FDCWD, *argv, atflag, mask, &stx);
25362306a36Sopenharmony_ci		printf("statx(%s) = %d\n", *argv, ret);
25462306a36Sopenharmony_ci		if (ret < 0) {
25562306a36Sopenharmony_ci			perror(*argv);
25662306a36Sopenharmony_ci			exit(1);
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (raw)
26062306a36Sopenharmony_ci			dump_hex((unsigned long long *)&stx, 0, sizeof(stx));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		dump_statx(&stx);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
266