|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2003-2005 Silicon Graphics, Inc. | 
|  | * All Rights Reserved. | 
|  | * Copyright (C) 2015, 2017 Red Hat, Inc. | 
|  | * Portions of statx support written by David Howells (dhowells@redhat.com) | 
|  | */ | 
|  |  | 
|  | #include "command.h" | 
|  | #include "input.h" | 
|  | #include "init.h" | 
|  | #include "io.h" | 
|  | #include "statx.h" | 
|  | #include "libxfs.h" | 
|  | #include "libfrog/logging.h" | 
|  | #include "libfrog/fsgeom.h" | 
|  |  | 
|  | #include <fcntl.h> | 
|  |  | 
|  | static cmdinfo_t stat_cmd; | 
|  | static cmdinfo_t statfs_cmd; | 
|  | static cmdinfo_t statx_cmd; | 
|  |  | 
|  | off64_t | 
|  | filesize(void) | 
|  | { | 
|  | struct stat	st; | 
|  |  | 
|  | if (fstat(file->fd, &st) < 0) { | 
|  | perror("fstat"); | 
|  | return -1; | 
|  | } | 
|  | return st.st_size; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | filetype(mode_t mode) | 
|  | { | 
|  | switch (mode & S_IFMT) { | 
|  | case S_IFSOCK: | 
|  | return _("socket"); | 
|  | case S_IFDIR: | 
|  | return _("directory"); | 
|  | case S_IFCHR: | 
|  | return _("char device"); | 
|  | case S_IFBLK: | 
|  | return _("block device"); | 
|  | case S_IFREG: | 
|  | return _("regular file"); | 
|  | case S_IFLNK: | 
|  | return _("symbolic link"); | 
|  | case S_IFIFO: | 
|  | return _("fifo"); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static int | 
|  | dump_raw_stat(struct stat *st) | 
|  | { | 
|  | printf("stat.blksize = %lu\n", (unsigned long)st->st_blksize); | 
|  | printf("stat.nlink = %lu\n", (unsigned long)st->st_nlink); | 
|  | printf("stat.uid = %u\n", st->st_uid); | 
|  | printf("stat.gid = %u\n", st->st_gid); | 
|  | printf("stat.mode: 0%o\n", st->st_mode); | 
|  | printf("stat.ino = %llu\n", (unsigned long long)st->st_ino); | 
|  | printf("stat.size = %lld\n", (long long)st->st_size); | 
|  | printf("stat.blocks = %lld\n", (long long)st->st_blocks); | 
|  | printf("stat.atime.tv_sec = %ld\n", st->st_atim.tv_sec); | 
|  | printf("stat.atime.tv_nsec = %ld\n", st->st_atim.tv_nsec); | 
|  | printf("stat.ctime.tv_sec = %ld\n", st->st_ctim.tv_sec); | 
|  | printf("stat.ctime.tv_nsec = %ld\n", st->st_ctim.tv_nsec); | 
|  | printf("stat.mtime.tv_sec = %ld\n", st->st_mtim.tv_sec); | 
|  | printf("stat.mtime.tv_nsec = %ld\n", st->st_mtim.tv_nsec); | 
|  | printf("stat.rdev_major = %u\n", major(st->st_rdev)); | 
|  | printf("stat.rdev_minor = %u\n", minor(st->st_rdev)); | 
|  | printf("stat.dev_major = %u\n", major(st->st_dev)); | 
|  | printf("stat.dev_minor = %u\n", minor(st->st_dev)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_file_info(void) | 
|  | { | 
|  | printf(_("fd.path = \"%s\"\n"), file->name); | 
|  | printf(_("fd.flags = %s,%s,%s%s%s%s%s\n"), | 
|  | file->flags & IO_OSYNC ? _("sync") : _("non-sync"), | 
|  | file->flags & IO_DIRECT ? _("direct") : _("non-direct"), | 
|  | file->flags & IO_READONLY ? _("read-only") : _("read-write"), | 
|  | file->flags & IO_REALTIME ? _(",real-time") : "", | 
|  | file->flags & IO_APPEND ? _(",append-only") : "", | 
|  | file->flags & IO_NONBLOCK ? _(",non-block") : "", | 
|  | file->flags & IO_TMPFILE ? _(",tmpfile") : ""); | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_xfs_info(int verbose) | 
|  | { | 
|  | struct dioattr	dio; | 
|  | struct fsxattr	fsx, fsxa; | 
|  |  | 
|  | if ((xfsctl(file->name, file->fd, FS_IOC_FSGETXATTR, &fsx)) < 0 || | 
|  | (xfsctl(file->name, file->fd, XFS_IOC_FSGETXATTRA, &fsxa)) < 0) { | 
|  | perror("FS_IOC_FSGETXATTR"); | 
|  | } else { | 
|  | printf(_("fsxattr.xflags = 0x%x "), fsx.fsx_xflags); | 
|  | printxattr(fsx.fsx_xflags, verbose, 0, file->name, 1, 1); | 
|  | printf(_("fsxattr.projid = %u\n"), fsx.fsx_projid); | 
|  | printf(_("fsxattr.extsize = %u\n"), fsx.fsx_extsize); | 
|  | printf(_("fsxattr.cowextsize = %u\n"), fsx.fsx_cowextsize); | 
|  | printf(_("fsxattr.nextents = %u\n"), fsx.fsx_nextents); | 
|  | printf(_("fsxattr.naextents = %u\n"), fsxa.fsx_nextents); | 
|  | } | 
|  | if ((xfsctl(file->name, file->fd, XFS_IOC_DIOINFO, &dio)) < 0) { | 
|  | perror("XFS_IOC_DIOINFO"); | 
|  | } else { | 
|  | printf(_("dioattr.mem = 0x%x\n"), dio.d_mem); | 
|  | printf(_("dioattr.miniosz = %u\n"), dio.d_miniosz); | 
|  | printf(_("dioattr.maxiosz = %u\n"), dio.d_maxiosz); | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | stat_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | struct stat	st; | 
|  | int		c, verbose = 0, raw = 0; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "rv")) != EOF) { | 
|  | switch (c) { | 
|  | case 'r': | 
|  | raw = 1; | 
|  | break; | 
|  | case 'v': | 
|  | verbose = 1; | 
|  | break; | 
|  | default: | 
|  | exitcode = 1; | 
|  | return command_usage(&stat_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (fstat(file->fd, &st) < 0) { | 
|  | perror("fstat"); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (raw) | 
|  | return dump_raw_stat(&st); | 
|  |  | 
|  | print_file_info(); | 
|  |  | 
|  | printf(_("stat.ino = %lld\n"), (long long)st.st_ino); | 
|  | printf(_("stat.type = %s\n"), filetype(st.st_mode)); | 
|  | printf(_("stat.size = %lld\n"), (long long)st.st_size); | 
|  | printf(_("stat.blocks = %lld\n"), (long long)st.st_blocks); | 
|  | if (verbose) { | 
|  | printf(_("stat.atime = %s"), ctime(&st.st_atime)); | 
|  | printf(_("stat.mtime = %s"), ctime(&st.st_mtime)); | 
|  | printf(_("stat.ctime = %s"), ctime(&st.st_ctime)); | 
|  | } | 
|  |  | 
|  | if (file->flags & IO_FOREIGN) | 
|  | return 0; | 
|  |  | 
|  | print_xfs_info(verbose); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | statfs_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " Display file system status.\n" | 
|  | "\n" | 
|  | " Options:\n" | 
|  | " -c -- Print fs summary count data.\n" | 
|  | " -g -- Print fs geometry data.\n" | 
|  | " -s -- Print statfs data.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | #define REPORT_STATFS		(1 << 0) | 
|  | #define REPORT_GEOMETRY		(1 << 1) | 
|  | #define REPORT_FSCOUNTS		(1 << 2) | 
|  |  | 
|  | static int | 
|  | statfs_f( | 
|  | int			argc, | 
|  | char			**argv) | 
|  | { | 
|  | struct xfs_fsop_counts	fscounts; | 
|  | struct xfs_fsop_geom	fsgeo; | 
|  | struct statfs		st; | 
|  | unsigned int		flags = 0; | 
|  | int			c; | 
|  | int			ret; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "cgs")) != EOF) { | 
|  | switch (c) { | 
|  | case 'c': | 
|  | flags |= REPORT_FSCOUNTS; | 
|  | break; | 
|  | case 'g': | 
|  | flags |= REPORT_GEOMETRY; | 
|  | break; | 
|  | case 's': | 
|  | flags |= REPORT_STATFS; | 
|  | break; | 
|  | default: | 
|  | exitcode = 1; | 
|  | return command_usage(&statfs_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!flags) | 
|  | flags = REPORT_STATFS | REPORT_GEOMETRY | REPORT_FSCOUNTS; | 
|  |  | 
|  | printf(_("fd.path = \"%s\"\n"), file->name); | 
|  | if (flags & REPORT_STATFS) { | 
|  | ret = platform_fstatfs(file->fd, &st); | 
|  | if (ret < 0) { | 
|  | perror("fstatfs"); | 
|  | exitcode = 1; | 
|  | } else { | 
|  | printf(_("statfs.f_bsize = %lld\n"), | 
|  | (long long) st.f_bsize); | 
|  | printf(_("statfs.f_blocks = %lld\n"), | 
|  | (long long) st.f_blocks); | 
|  | printf(_("statfs.f_bavail = %lld\n"), | 
|  | (long long) st.f_bavail); | 
|  | printf(_("statfs.f_files = %lld\n"), | 
|  | (long long) st.f_files); | 
|  | printf(_("statfs.f_ffree = %lld\n"), | 
|  | (long long) st.f_ffree); | 
|  | #ifdef HAVE_STATFS_FLAGS | 
|  | printf(_("statfs.f_flags = 0x%llx\n"), | 
|  | (long long) st.f_flags); | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | if (file->flags & IO_FOREIGN) | 
|  | return 0; | 
|  |  | 
|  | if (flags & REPORT_GEOMETRY) { | 
|  | ret = -xfrog_geometry(file->fd, &fsgeo); | 
|  | if (ret) { | 
|  | xfrog_perror(ret, "XFS_IOC_FSGEOMETRY"); | 
|  | exitcode = 1; | 
|  | } else { | 
|  | printf(_("geom.bsize = %u\n"), fsgeo.blocksize); | 
|  | printf(_("geom.agcount = %u\n"), fsgeo.agcount); | 
|  | printf(_("geom.agblocks = %u\n"), fsgeo.agblocks); | 
|  | printf(_("geom.datablocks = %llu\n"), | 
|  | (unsigned long long) fsgeo.datablocks); | 
|  | printf(_("geom.rtblocks = %llu\n"), | 
|  | (unsigned long long) fsgeo.rtblocks); | 
|  | printf(_("geom.rtextents = %llu\n"), | 
|  | (unsigned long long) fsgeo.rtextents); | 
|  | printf(_("geom.rtextsize = %u\n"), fsgeo.rtextsize); | 
|  | printf(_("geom.sunit = %u\n"), fsgeo.sunit); | 
|  | printf(_("geom.swidth = %u\n"), fsgeo.swidth); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (flags & REPORT_FSCOUNTS) { | 
|  | ret = ioctl(file->fd, XFS_IOC_FSCOUNTS, &fscounts); | 
|  | if (ret < 0) { | 
|  | perror("XFS_IOC_FSCOUNTS"); | 
|  | exitcode = 1; | 
|  | } else { | 
|  | printf(_("counts.freedata = %llu\n"), | 
|  | (unsigned long long) fscounts.freedata); | 
|  | printf(_("counts.freertx = %llu\n"), | 
|  | (unsigned long long) fscounts.freertx); | 
|  | printf(_("counts.freeino = %llu\n"), | 
|  | (unsigned long long) fscounts.freeino); | 
|  | printf(_("counts.allocino = %llu\n"), | 
|  | (unsigned long long) fscounts.allocino); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t | 
|  | _statx( | 
|  | int		dfd, | 
|  | const char	*filename, | 
|  | unsigned int	flags, | 
|  | unsigned int	mask, | 
|  | struct statx	*buffer) | 
|  | { | 
|  | #ifdef __NR_statx | 
|  | return syscall(__NR_statx, dfd, filename, flags, mask, buffer); | 
|  | #else | 
|  | errno = ENOSYS; | 
|  | return -1; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static void | 
|  | statx_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " Display extended file status.\n" | 
|  | "\n" | 
|  | " Options:\n" | 
|  | " -v -- More verbose output\n" | 
|  | " -r -- Print raw statx structure fields\n" | 
|  | " -m mask -- Specify the field mask for the statx call\n" | 
|  | "            (can also be 'basic' or 'all'; default STATX_ALL)\n" | 
|  | " -D -- Don't sync attributes with the server\n" | 
|  | " -F -- Force the attributes to be sync'd with the server\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | /* statx helper */ | 
|  | static int | 
|  | dump_raw_statx(struct statx *stx) | 
|  | { | 
|  | printf("stat.mask = 0x%x\n", stx->stx_mask); | 
|  | printf("stat.blksize = %u\n", stx->stx_blksize); | 
|  | printf("stat.attributes = 0x%llx\n", (unsigned long long)stx->stx_attributes); | 
|  | printf("stat.nlink = %u\n", stx->stx_nlink); | 
|  | printf("stat.uid = %u\n", stx->stx_uid); | 
|  | printf("stat.gid = %u\n", stx->stx_gid); | 
|  | printf("stat.mode: 0%o\n", stx->stx_mode); | 
|  | printf("stat.ino = %llu\n", (unsigned long long)stx->stx_ino); | 
|  | printf("stat.size = %llu\n", (unsigned long long)stx->stx_size); | 
|  | printf("stat.blocks = %llu\n", (unsigned long long)stx->stx_blocks); | 
|  | printf("stat.attributes_mask = 0x%llx\n", (unsigned long long)stx->stx_attributes_mask); | 
|  | printf("stat.atime.tv_sec = %lld\n", (long long)stx->stx_atime.tv_sec); | 
|  | printf("stat.atime.tv_nsec = %d\n", stx->stx_atime.tv_nsec); | 
|  | printf("stat.btime.tv_sec = %lld\n", (long long)stx->stx_btime.tv_sec); | 
|  | printf("stat.btime.tv_nsec = %d\n", stx->stx_btime.tv_nsec); | 
|  | printf("stat.ctime.tv_sec = %lld\n", (long long)stx->stx_ctime.tv_sec); | 
|  | printf("stat.ctime.tv_nsec = %d\n", stx->stx_ctime.tv_nsec); | 
|  | printf("stat.mtime.tv_sec = %lld\n", (long long)stx->stx_mtime.tv_sec); | 
|  | printf("stat.mtime.tv_nsec = %d\n", stx->stx_mtime.tv_nsec); | 
|  | printf("stat.rdev_major = %u\n", stx->stx_rdev_major); | 
|  | printf("stat.rdev_minor = %u\n", stx->stx_rdev_minor); | 
|  | printf("stat.dev_major = %u\n", stx->stx_dev_major); | 
|  | printf("stat.dev_minor = %u\n", stx->stx_dev_minor); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * options: | 
|  | * 	- input flags - query type | 
|  | * 	- output style for flags (and all else?) (chars vs. hex?) | 
|  | * 	- output - mask out incidental flag or not? | 
|  | */ | 
|  | static int | 
|  | statx_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | int		c, verbose = 0, raw = 0; | 
|  | char		*p; | 
|  | struct statx	stx; | 
|  | int		atflag = 0; | 
|  | unsigned int	mask = STATX_ALL; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "m:rvFD")) != EOF) { | 
|  | switch (c) { | 
|  | case 'm': | 
|  | if (strcmp(optarg, "basic") == 0) | 
|  | mask = STATX_BASIC_STATS; | 
|  | else if (strcmp(optarg, "all") == 0) | 
|  | mask = STATX_ALL; | 
|  | else { | 
|  | mask = strtoul(optarg, &p, 0); | 
|  | if (!p || p == optarg) { | 
|  | printf( | 
|  | _("non-numeric mask -- %s\n"), optarg); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case 'r': | 
|  | raw = 1; | 
|  | break; | 
|  | case 'v': | 
|  | verbose = 1; | 
|  | break; | 
|  | case 'F': | 
|  | atflag &= ~AT_STATX_SYNC_TYPE; | 
|  | atflag |= AT_STATX_FORCE_SYNC; | 
|  | break; | 
|  | case 'D': | 
|  | atflag &= ~AT_STATX_SYNC_TYPE; | 
|  | atflag |= AT_STATX_DONT_SYNC; | 
|  | break; | 
|  | default: | 
|  | exitcode = 1; | 
|  | return command_usage(&statx_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (raw && verbose) | 
|  | return command_usage(&statx_cmd); | 
|  |  | 
|  | memset(&stx, 0xbf, sizeof(stx)); | 
|  | if (_statx(file->fd, "", atflag | AT_EMPTY_PATH, mask, &stx) < 0) { | 
|  | perror("statx"); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | exitcode = 0; | 
|  |  | 
|  | if (raw) | 
|  | return dump_raw_statx(&stx); | 
|  |  | 
|  | print_file_info(); | 
|  |  | 
|  | printf(_("stat.ino = %lld\n"), (long long)stx.stx_ino); | 
|  | printf(_("stat.type = %s\n"), filetype(stx.stx_mode)); | 
|  | printf(_("stat.size = %lld\n"), (long long)stx.stx_size); | 
|  | printf(_("stat.blocks = %lld\n"), (long long)stx.stx_blocks); | 
|  | if (verbose) { | 
|  | printf(_("stat.atime = %s"), ctime((time_t *)&stx.stx_atime.tv_sec)); | 
|  | printf(_("stat.mtime = %s"), ctime((time_t *)&stx.stx_mtime.tv_sec)); | 
|  | printf(_("stat.ctime = %s"), ctime((time_t *)&stx.stx_ctime.tv_sec)); | 
|  | if (stx.stx_mask & STATX_BTIME) | 
|  | printf(_("stat.btime = %s"), | 
|  | ctime((time_t *)&stx.stx_btime.tv_sec)); | 
|  | } | 
|  |  | 
|  | if (file->flags & IO_FOREIGN) | 
|  | return 0; | 
|  |  | 
|  | print_xfs_info(verbose); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | stat_init(void) | 
|  | { | 
|  | stat_cmd.name = "stat"; | 
|  | stat_cmd.cfunc = stat_f; | 
|  | stat_cmd.argmin = 0; | 
|  | stat_cmd.argmax = 1; | 
|  | stat_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | stat_cmd.args = _("[-v|-r]"); | 
|  | stat_cmd.oneline = _("statistics on the currently open file"); | 
|  |  | 
|  | statx_cmd.name = "statx"; | 
|  | statx_cmd.cfunc = statx_f; | 
|  | statx_cmd.argmin = 0; | 
|  | statx_cmd.argmax = -1; | 
|  | statx_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | statx_cmd.args = _("[-v|-r][-m basic | -m all | -m <mask>][-FD]"); | 
|  | statx_cmd.oneline = _("extended statistics on the currently open file"); | 
|  | statx_cmd.help = statx_help; | 
|  |  | 
|  | statfs_cmd.name = "statfs"; | 
|  | statfs_cmd.cfunc = statfs_f; | 
|  | statfs_cmd.argmin = 0; | 
|  | statfs_cmd.argmax = -1; | 
|  | statfs_cmd.args = _("[-c] [-g] [-s]"); | 
|  | statfs_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | statfs_cmd.oneline = | 
|  | _("statistics on the filesystem of the currently open file"); | 
|  | statfs_cmd.help = statfs_help; | 
|  |  | 
|  | add_command(&stat_cmd); | 
|  | add_command(&statx_cmd); | 
|  | add_command(&statfs_cmd); | 
|  | } |