// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2000-2003 Silicon Graphics, Inc. * All Rights Reserved. */ /* * Test for filesystem features on given mount point or device * -c test for 32bit chown support (via libc) * -t test for working rlimit/ftruncate64 (via libc) * -q test for quota support (kernel compile option) * -u test for user quota enforcement support (mount option) * -p test for project quota enforcement support (mount option) * -g test for group quota enforcement support (mount option) * -U test for user quota accounting support (mount option) * -G test for group quota accounting support (mount option) * -P test for project quota accounting support (mount option) * Return code: 0 is true, anything else is error/not supported * * Test for machine features * -A test whether AIO syscalls are available * -r test whether mount_setattr syscall is supported * -R test whether IO_URING syscalls are available * -o report a number of online cpus * -s report pagesize * -w report bits per long */ #include "global.h" #include #include #include #include #include #ifdef HAVE_XFS_XQM_H #include #endif #ifdef HAVE_LIBAIO_H #include #endif #ifdef HAVE_LIBURING_H #include #endif #include "vfs/missing.h" #ifndef USRQUOTA #define USRQUOTA 0 #endif #ifndef GRPQUOTA #define GRPQUOTA 1 #endif #ifndef PRJQUOTA #define PRJQUOTA 2 #endif int verbose = 0; void usage(void) { fprintf(stderr, "Usage: feature [-v] - \n"); fprintf(stderr, " feature [-v] -c \n"); fprintf(stderr, " feature [-v] -t \n"); fprintf(stderr, " feature -A | -r | -R | -o | -s | -w\n"); exit(1); } int check_big_ID(char *filename) { struct stat64 sbuf; memset(&sbuf, 0, sizeof(struct stat64)); if (lstat64(filename, &sbuf) < 0) { fprintf(stderr, "lstat64 failed on "); perror(filename); return(1); } /* 98789 is greater than 2^16 (65536) */ if ((uint32_t)sbuf.st_uid == 98789 || (uint32_t)sbuf.st_gid == 98789) return(0); if (verbose) fprintf(stderr, "lstat64 on %s gave uid=%d, gid=%d\n", filename, (int)sbuf.st_uid, (int)sbuf.st_gid); return(1); } int haschown32(char *filename) { if (check_big_ID(filename) == 0) return(0); if (chown(filename, 98789, 98789) < 0) { fprintf(stderr, "chown failed on "); perror(filename); return(1); } if (check_big_ID(filename) == 0) return(0); return (1); } int hastruncate64(char *filename) { struct rlimit64 rlimit64; off64_t bigoff = 4294967307LL; /* > 2^32 */ struct stat64 bigst; int fd; getrlimit64(RLIMIT_FSIZE, &rlimit64); rlimit64.rlim_cur = RLIM64_INFINITY; setrlimit64(RLIMIT_FSIZE, &rlimit64); signal(SIGXFSZ, SIG_IGN); if ((fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) { fprintf(stderr, "open failed on "); perror(filename); return(1); } if (ftruncate64(fd, bigoff) < 0) return(1); if (fstat64(fd, &bigst) < 0) { fprintf(stderr, "fstat64 failed on "); perror(filename); return(1); } if (verbose) fprintf(stderr, "fstat64 on %s gave sz=%lld (truncsz=%lld)\n", filename, (long long)bigst.st_size, (long long)bigoff); if (bigst.st_size != bigoff) return(1); return(0); } int hasxfsquota(int type, int q, char *device) { fs_quota_stat_t qstat; int qcmd; memset(&qstat, 0, sizeof(fs_quota_stat_t)); #ifdef QCMD if (q == 0) { if (access("/proc/fs/xfs/xqm", F_OK) < 0) { if (verbose) { fprintf(stderr, "can't access /proc/fs/xfs/xqm\n"); } return 1; } return 0; } qcmd = QCMD(Q_XGETQSTAT, type); #else if (q == 0) { if (quotactl(Q_SYNC, device, 0, (caddr_t)&qstat) == ENOPKG) { if (verbose) { fprintf(stderr, "Q_SYNC not supported\n"); } return 1; } return 0; } qcmd = Q_GETQSTAT; #endif if (quotactl(qcmd, device, 0, (caddr_t)&qstat) < 0) { if (verbose) perror("quotactl"); return (1); } else if (q == XFS_QUOTA_UDQ_ENFD && qstat.qs_flags & XFS_QUOTA_UDQ_ENFD) return (0); else if (q == XFS_QUOTA_GDQ_ENFD && qstat.qs_flags & XFS_QUOTA_GDQ_ENFD) return (0); else if (q == XFS_QUOTA_PDQ_ENFD && qstat.qs_flags & XFS_QUOTA_PDQ_ENFD) return (0); else if (q == XFS_QUOTA_UDQ_ACCT && qstat.qs_flags & XFS_QUOTA_UDQ_ACCT) return (0); else if (q == XFS_QUOTA_GDQ_ACCT && qstat.qs_flags & XFS_QUOTA_GDQ_ACCT) return (0); else if (q == XFS_QUOTA_PDQ_ACCT && qstat.qs_flags & XFS_QUOTA_PDQ_ACCT) return (0); if (verbose) fprintf(stderr, "quota type (%d) not available\n", q); return (1); } static int check_aio_support(void) { #ifdef HAVE_LIBAIO_H struct io_context *ctx = NULL; int err; err = io_setup(1, &ctx); if (err == 0) return 0; if (err == -ENOSYS) /* CONFIG_AIO=n */ return 1; fprintf(stderr, "unexpected error from io_setup(): %s\n", strerror(-err)); return 2; #else /* libaio was unavailable at build time; assume AIO is unsupported */ return 1; #endif } static int check_uring_support(void) { #ifdef HAVE_LIBURING_H struct io_uring ring; int err; err = io_uring_queue_init(1, &ring, 0); if (err == 0) return 0; if (err == -ENOSYS) /* CONFIG_IO_URING=n */ return 1; fprintf(stderr, "unexpected error from io_uring_queue_init(): %s\n", strerror(-err)); return 2; #else /* liburing is unavailable, assume IO_URING is unsupported */ return 1; #endif } static int check_mount_setattr_support(void) { int err; struct mount_attr attr = { .attr_set = MOUNT_ATTR_IDMAP, .userns_fd = -EBADF, }; /* mount_setattr() syscall not supported. */ err = sys_mount_setattr(-EBADF, "", AT_EMPTY_PATH, NULL, 0); if (err && errno == ENOSYS) return 1; /* idmapped mounts not supported */ err = sys_mount_setattr(-EBADF, ".", AT_EMPTY_PATH, &attr, sizeof(attr)); if (err && errno == E2BIG) return 1; return 0; } int main(int argc, char **argv) { int c; int Aflag = 0; int cflag = 0; int tflag = 0; int gflag = 0; int Gflag = 0; int pflag = 0; int Pflag = 0; int qflag = 0; int rflag = 0; int Rflag = 0; int sflag = 0; int uflag = 0; int Uflag = 0; int wflag = 0; int oflag = 0; char *fs = NULL; while ((c = getopt(argc, argv, "ActgGopPqrRsuUvw")) != EOF) { switch (c) { case 'A': Aflag++; break; case 'c': cflag++; break; case 't': tflag++; break; case 'g': gflag++; break; case 'G': Gflag++; break; case 'o': oflag++; break; case 'p': pflag++; break; case 'P': Pflag++; break; case 'q': qflag++; break; case 'r': rflag++; break; case 'R': Rflag++; break; case 's': sflag++; break; case 'u': uflag++; break; case 'U': Uflag++; break; case 'w': wflag++; break; case 'v': verbose++; break; default: usage(); } } /* filesystem features */ if (cflag|tflag|uflag|gflag|pflag|qflag|Uflag|Gflag|Pflag) { if (optind != argc-1) /* need a device */ usage(); fs = argv[argc-1]; } else if (Aflag || rflag || Rflag || wflag || sflag || oflag) { if (optind != argc) usage(); } else usage(); if (cflag) return(haschown32(fs)); if (tflag) return(hastruncate64(fs)); if (qflag) return(hasxfsquota(0, 0, fs)); if (gflag) return(hasxfsquota(GRPQUOTA, XFS_QUOTA_GDQ_ENFD, fs)); if (pflag) return(hasxfsquota(PRJQUOTA, XFS_QUOTA_PDQ_ENFD, fs)); if (uflag) return(hasxfsquota(USRQUOTA, XFS_QUOTA_UDQ_ENFD, fs)); if (Gflag) return(hasxfsquota(GRPQUOTA, XFS_QUOTA_GDQ_ACCT, fs)); if (Pflag) return(hasxfsquota(PRJQUOTA, XFS_QUOTA_PDQ_ACCT, fs)); if (Uflag) return(hasxfsquota(USRQUOTA, XFS_QUOTA_UDQ_ACCT, fs)); if (Aflag) return(check_aio_support()); if (rflag) return(check_mount_setattr_support()); if (Rflag) return(check_uring_support()); if (sflag) { printf("%d\n", getpagesize()); exit(0); } if (wflag) { #ifdef BITS_PER_LONG printf("%d\n", BITS_PER_LONG); #else #ifdef NBBY /* This can change under IRIX depending on whether we compile * with -n32/-32 or -64 */ printf("%d\n", (int)(NBBY * sizeof(long))); #else bozo! #endif #endif exit(0); } if (oflag) { long ncpus = -1; #if defined(_SC_NPROCESSORS_ONLN) ncpus = sysconf(_SC_NPROCESSORS_ONLN); #elif defined(_SC_NPROC_ONLN) ncpus = sysconf(_SC_NPROC_ONLN); #endif if (ncpus == -1) ncpus = 1; printf("%ld\n", ncpus); exit(0); } fprintf(stderr, "feature: dunno what you're after.\n"); return(1); }