/* Test the fsinfo() system call * * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@xxxxxxxxxx) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public Licence * as published by the Free Software Foundation; either version * 2 of the Licence, or (at your option) any later version. */ #define _GNU_SOURCE #define _ATFILE_SOURCE #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <errno.h> #include <time.h> #include <math.h> #include <fcntl.h> #include <sys/syscall.h> #include <linux/stat.h> #include <linux/socket.h> #include <sys/stat.h> #define __NR_fsinfo 338 enum fsinfo_attribute { fsinfo_attr_statfs = 0, /* statfs()-style state */ fsinfo_attr_fsinfo = 1, /* Information about fsinfo() */ fsinfo_attr_ids = 2, /* Filesystem IDs */ fsinfo_attr_limits = 3, /* Filesystem limits */ fsinfo_attr_supports = 4, /* What's supported in statx, iocflags, ... */ fsinfo_attr_capabilities = 5, /* Filesystem capabilities (bits) */ fsinfo_attr_timestamp_info = 6, /* Inode timestamp info */ fsinfo_attr_volume_id = 7, /* Volume ID (string) */ fsinfo_attr_volume_uuid = 8, /* Volume UUID (LE uuid) */ fsinfo_attr_volume_name = 9, /* Volume name (string) */ fsinfo_attr_cell_name = 10, /* Cell name (string) */ fsinfo_attr_domain_name = 11, /* Domain name (string) */ fsinfo_attr_realm_name = 12, /* Realm name (string) */ fsinfo_attr_server_name = 13, /* Name of the Nth server */ fsinfo_attr_server_address = 14, /* Mth address of the Nth server */ fsinfo_attr_error_state = 15, /* Error state */ fsinfo_attr_parameter = 16, /* Nth mount parameter (string) */ fsinfo_attr_source = 17, /* Nth mount source name (string) */ fsinfo_attr_name_encoding = 18, /* Filename encoding (string) */ fsinfo_attr_name_codepage = 19, /* Filename codepage (string) */ fsinfo_attr_io_size = 20, /* Optimal I/O sizes */ fsinfo_attr__nr }; struct fsinfo_params { enum fsinfo_attribute request; /* What is being asking for */ __u32 Nth; /* Instance of it (some may have multiple) */ __u32 Mth; /* Subinstance of Nth instance */ __u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar flags */ __u32 __reserved[6]; /* Reserved params; all must be 0 */ }; struct fsinfo_statfs { __u64 f_blocks; /* Total number of blocks in fs */ __u64 f_bfree; /* Total number of free blocks */ __u64 f_bavail; /* Number of free blocks available to ordinary user */ __u64 f_files; /* Total number of file nodes in fs */ __u64 f_ffree; /* Number of free file nodes */ __u64 f_favail; /* Number of free file nodes available to ordinary user */ __u32 f_bsize; /* Optimal block size */ __u32 f_frsize; /* Fragment size */ }; struct fsinfo_ids { char f_fs_name[15 + 1]; __u64 f_flags; /* Filesystem mount flags (MS_*) */ __u64 f_fsid; /* Short 64-bit Filesystem ID (as statfs) */ __u64 f_sb_id; /* Internal superblock ID for sbnotify()/mntnotify() */ __u32 f_fstype; /* Filesystem type from linux/magic.h [uncond] */ __u32 f_dev_major; /* As st_dev_* from struct statx [uncond] */ __u32 f_dev_minor; }; struct fsinfo_limits { __u64 max_file_size; /* Maximum file size */ __u64 max_uid; /* Maximum UID supported */ __u64 max_gid; /* Maximum GID supported */ __u64 max_projid; /* Maximum project ID supported */ __u32 max_dev_major; /* Maximum device major representable */ __u32 max_dev_minor; /* Maximum device minor representable */ __u32 max_hard_links; /* Maximum number of hard links on a file */ __u32 max_xattr_body_len; /* Maximum xattr content length */ __u16 max_xattr_name_len; /* Maximum xattr name length */ __u16 max_filename_len; /* Maximum filename length */ __u16 max_symlink_len; /* Maximum symlink content length */ __u16 __spare; }; struct fsinfo_supports { __u64 supported_stx_attributes; /* What statx::stx_attributes are supported */ __u32 supported_stx_mask; /* What statx::stx_mask bits are supported */ __u32 supported_ioc_flags; /* What FS_IOC_* flags are supported */ }; enum fsinfo_capability { fsinfo_cap_is_kernel_fs = 0, /* fs is kernel-special filesystem */ fsinfo_cap_is_block_fs = 1, /* fs is block-based filesystem */ fsinfo_cap_is_flash_fs = 2, /* fs is flash filesystem */ fsinfo_cap_is_network_fs = 3, /* fs is network filesystem */ fsinfo_cap_is_automounter_fs = 4, /* fs is automounter special filesystem */ fsinfo_cap_automounts = 5, /* fs supports automounts */ fsinfo_cap_adv_locks = 6, /* fs supports advisory file locking */ fsinfo_cap_mand_locks = 7, /* fs supports mandatory file locking */ fsinfo_cap_leases = 8, /* fs supports file leases */ fsinfo_cap_uids = 9, /* fs supports numeric uids */ fsinfo_cap_gids = 10, /* fs supports numeric gids */ fsinfo_cap_projids = 11, /* fs supports numeric project ids */ fsinfo_cap_id_names = 12, /* fs supports user names */ fsinfo_cap_id_guids = 13, /* fs supports user guids */ fsinfo_cap_windows_attrs = 14, /* fs has windows attributes */ fsinfo_cap_user_quotas = 15, /* fs has per-user quotas */ fsinfo_cap_group_quotas = 16, /* fs has per-group quotas */ fsinfo_cap_project_quotas = 17, /* fs has per-project quotas */ fsinfo_cap_xattrs = 18, /* fs has xattrs */ fsinfo_cap_journal = 19, /* fs has a journal */ fsinfo_cap_data_is_journalled = 20, /* fs is using data journalling */ fsinfo_cap_o_sync = 21, /* fs supports O_SYNC */ fsinfo_cap_o_direct = 22, /* fs supports O_DIRECT */ fsinfo_cap_volume_id = 23, /* fs has a volume ID */ fsinfo_cap_volume_uuid = 24, /* fs has a volume UUID */ fsinfo_cap_volume_name = 25, /* fs has a volume name */ fsinfo_cap_volume_fsid = 26, /* fs has a volume FSID */ fsinfo_cap_cell_name = 27, /* fs has a cell name */ fsinfo_cap_domain_name = 28, /* fs has a domain name */ fsinfo_cap_realm_name = 29, /* fs has a realm name */ fsinfo_cap_iver_all_change = 30, /* i_version represents data + meta changes */ fsinfo_cap_iver_data_change = 31, /* i_version represents data changes only */ fsinfo_cap_iver_mono_incr = 32, /* i_version incremented monotonically */ fsinfo_cap_symlinks = 33, /* fs supports symlinks */ fsinfo_cap_hard_links = 34, /* fs supports hard links */ fsinfo_cap_hard_links_1dir = 35, /* fs supports hard links in same dir only */ fsinfo_cap_device_files = 36, /* fs supports bdev, cdev */ fsinfo_cap_unix_specials = 37, /* fs supports pipe, fifo, socket */ fsinfo_cap_resource_forks = 38, /* fs supports resource forks/streams */ fsinfo_cap_name_case_indep = 39, /* Filename case independence is mandatory */ fsinfo_cap_name_non_utf8 = 40, /* fs has non-utf8 names */ fsinfo_cap_name_has_codepage = 41, /* fs has a filename codepage */ fsinfo_cap_sparse = 42, /* fs supports sparse files */ fsinfo_cap_not_persistent = 43, /* fs is not persistent */ fsinfo_cap_no_unix_mode = 44, /* fs does not support unix mode bits */ fsinfo_cap_has_atime = 45, /* fs supports access time */ fsinfo_cap_has_btime = 46, /* fs supports birth/creation time */ fsinfo_cap_has_ctime = 47, /* fs supports change time */ fsinfo_cap_has_mtime = 48, /* fs supports modification time */ fsinfo_cap__nr }; struct fsinfo_capabilities { __u8 capabilities[(fsinfo_cap__nr + 7) / 8]; }; struct fsinfo_timestamp_info { __s64 minimum_timestamp; /* Minimum timestamp value in seconds */ __s64 maximum_timestamp; /* Maximum timestamp value in seconds */ __u16 atime_gran_mantissa; /* Granularity(secs) = mant * 10^exp */ __u16 btime_gran_mantissa; __u16 ctime_gran_mantissa; __u16 mtime_gran_mantissa; __s8 atime_gran_exponent; __s8 btime_gran_exponent; __s8 ctime_gran_exponent; __s8 mtime_gran_exponent; }; struct fsinfo_volume_uuid { __u8 uuid[16]; }; struct fsinfo_server_address { struct __kernel_sockaddr_storage address; }; struct fsinfo_error_state { __u32 io_error; /* General I/O error counter */ __u32 wb_error; /* Writeback error counter */ __u32 bdev_error; /* Blockdev error counter */ }; struct fsinfo_io_size { __u32 block_size; /* Minimum block granularity for O_DIRECT */ __u32 max_single_read_size; /* Maximum size of a single unbuffered read */ __u32 max_single_write_size; /* Maximum size of a single unbuffered write */ __u32 best_read_size; /* Optimal read size */ __u32 best_write_size; /* Optimal write size */ }; struct fsinfo_fsinfo { enum fsinfo_attribute max_attr; /* Number of supported attributes */ enum fsinfo_capability max_cap; /* Number of supported capabilities */ }; /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ static __attribute__((unused)) ssize_t fsinfo(int dfd, const char *filename, struct fsinfo_params *params, void *buffer, size_t buf_size) { return syscall(__NR_fsinfo, dfd, filename, params, buffer, buf_size); } #define FSINFO_STRING(N) [fsinfo_attr_##N] = 0x00 #define FSINFO_STRUCT(N) [fsinfo_attr_##N] = sizeof(struct fsinfo_##N)/sizeof(__u32) #define FSINFO_STRING_N(N) [fsinfo_attr_##N] = 0x40 #define FSINFO_STRUCT_N(N) [fsinfo_attr_##N] = 0x40 | sizeof(struct fsinfo_##N)/sizeof(__u32) #define FSINFO_STRUCT_NM(N) [fsinfo_attr_##N] = 0x80 | sizeof(struct fsinfo_##N)/sizeof(__u32) static const __u8 fsinfo_buffer_sizes[fsinfo_attr__nr] = { FSINFO_STRUCT (statfs), FSINFO_STRUCT (fsinfo), FSINFO_STRUCT (ids), FSINFO_STRUCT (limits), FSINFO_STRUCT (supports), FSINFO_STRUCT (capabilities), FSINFO_STRUCT (timestamp_info), FSINFO_STRING (volume_id), FSINFO_STRUCT (volume_uuid), FSINFO_STRING (volume_name), FSINFO_STRING (cell_name), FSINFO_STRING (domain_name), FSINFO_STRING (realm_name), FSINFO_STRING_N (server_name), FSINFO_STRUCT_NM (server_address), FSINFO_STRUCT (error_state), FSINFO_STRING_N (parameter), FSINFO_STRING_N (source), FSINFO_STRING (name_encoding), FSINFO_STRING (name_codepage), FSINFO_STRUCT (io_size), }; #define FSINFO_NAME(N) [fsinfo_attr_##N] = #N static const char *fsinfo_attr_names[fsinfo_attr__nr] = { FSINFO_NAME(statfs), FSINFO_NAME(fsinfo), FSINFO_NAME(ids), FSINFO_NAME(limits), FSINFO_NAME(supports), FSINFO_NAME(capabilities), FSINFO_NAME(timestamp_info), FSINFO_NAME(volume_id), FSINFO_NAME(volume_uuid), FSINFO_NAME(volume_name), FSINFO_NAME(cell_name), FSINFO_NAME(domain_name), FSINFO_NAME(realm_name), FSINFO_NAME(server_name), FSINFO_NAME(server_address), FSINFO_NAME(error_state), FSINFO_NAME(parameter), FSINFO_NAME(source), FSINFO_NAME(name_encoding), FSINFO_NAME(name_codepage), FSINFO_NAME(io_size), }; union reply { char buffer[4096]; struct fsinfo_statfs statfs; struct fsinfo_fsinfo fsinfo; struct fsinfo_ids ids; struct fsinfo_limits limits; struct fsinfo_supports supports; struct fsinfo_capabilities caps; struct fsinfo_timestamp_info timestamps; struct fsinfo_volume_uuid uuid; struct fsinfo_server_address srv_addr; struct fsinfo_error_state errors; struct fsinfo_io_size io_size; }; /* * Dump as hex. */ static void dump_hex(unsigned int *data, int from, int to) { unsigned offset, print_offset = 1, col = 0; from /= 4; to = (to + 3) / 4; for (offset = from; offset < to; offset++) { if (print_offset) { printf("%04x: ", offset * 8); print_offset = 0; } printf("%08x", data[offset]); col++; if ((col & 3) == 0) { printf("\n"); print_offset = 1; } else { printf(" "); } } if (!print_offset) printf("\n"); } #if 0 static void dump_fsinfo(struct fsinfo *f) { printf("ioc : %llx\n", (unsigned long long)f->f_supported_ioc_flags); if (f->f_mask & FSINFO_VOLUME_ID) { int printable = 1, loop; printf("volid : "); for (loop = 0; loop < sizeof(f->f_volume_id); loop++) if (!isprint(f->f_volume_id[loop])) printable = 0; if (printable) { printf("'%.*s'", 16, f->f_volume_id); } else { for (loop = 0; loop < sizeof(f->f_volume_id); loop++) { if (loop % 4 == 0 && loop != 0) printf(" "); printf("%02x", f->f_volume_id[loop]); } } printf("\n"); } } #endif static void dump_attr_statfs(union reply *r, int size) { struct fsinfo_statfs *f = &r->statfs; printf("\tblocks: n=%llu fr=%llu av=%llu\n", (unsigned long long)f->f_blocks, (unsigned long long)f->f_bfree, (unsigned long long)f->f_bavail); printf("\tfiles : n=%llu fr=%llu av=%llu\n", (unsigned long long)f->f_files, (unsigned long long)f->f_ffree, (unsigned long long)f->f_favail); printf("\tbsize : %u\n", f->f_bsize); printf("\tfrsize: %u\n", f->f_frsize); } static void dump_attr_fsinfo(union reply *r, int size) { struct fsinfo_fsinfo *f = &r->fsinfo; printf("max_attr=%u max_cap=%u\n", f->max_attr, f->max_cap); } static void dump_attr_ids(union reply *r, int size) { struct fsinfo_ids *f = &r->ids; printf("dev : %02x:%02x\n", f->f_dev_major, f->f_dev_minor); printf("\tfs : type=%x name=%s\n", f->f_fstype, f->f_fs_name); printf("\tflags : %llx\n", (unsigned long long)f->f_flags); printf("\tfsid : %llx\n", (unsigned long long)f->f_fsid); } static void dump_attr_limits(union reply *r, int size) { struct fsinfo_limits *f = &r->limits; printf("max file size: %llx\n", f->max_file_size); } static void dump_attr_supports(union reply *r, int size) { struct fsinfo_supports *f = &r->supports; printf("stx_attr=%llx\n", f->supported_stx_attributes); } #define FSINFO_CAP_NAME(C) [fsinfo_cap_##C] = #C static const char *fsinfo_cap_names[fsinfo_cap__nr] = { FSINFO_CAP_NAME(is_kernel_fs), FSINFO_CAP_NAME(is_block_fs), FSINFO_CAP_NAME(is_flash_fs), FSINFO_CAP_NAME(is_network_fs), FSINFO_CAP_NAME(is_automounter_fs), FSINFO_CAP_NAME(automounts), FSINFO_CAP_NAME(adv_locks), FSINFO_CAP_NAME(mand_locks), FSINFO_CAP_NAME(leases), FSINFO_CAP_NAME(uids), FSINFO_CAP_NAME(gids), FSINFO_CAP_NAME(projids), FSINFO_CAP_NAME(id_names), FSINFO_CAP_NAME(id_guids), FSINFO_CAP_NAME(windows_attrs), FSINFO_CAP_NAME(user_quotas), FSINFO_CAP_NAME(group_quotas), FSINFO_CAP_NAME(project_quotas), FSINFO_CAP_NAME(xattrs), FSINFO_CAP_NAME(journal), FSINFO_CAP_NAME(data_is_journalled), FSINFO_CAP_NAME(o_sync), FSINFO_CAP_NAME(o_direct), FSINFO_CAP_NAME(volume_id), FSINFO_CAP_NAME(volume_uuid), FSINFO_CAP_NAME(volume_name), FSINFO_CAP_NAME(volume_fsid), FSINFO_CAP_NAME(cell_name), FSINFO_CAP_NAME(domain_name), FSINFO_CAP_NAME(realm_name), FSINFO_CAP_NAME(iver_all_change), FSINFO_CAP_NAME(iver_data_change), FSINFO_CAP_NAME(iver_mono_incr), FSINFO_CAP_NAME(symlinks), FSINFO_CAP_NAME(hard_links), FSINFO_CAP_NAME(hard_links_1dir), FSINFO_CAP_NAME(device_files), FSINFO_CAP_NAME(unix_specials), FSINFO_CAP_NAME(resource_forks), FSINFO_CAP_NAME(name_case_indep), FSINFO_CAP_NAME(name_non_utf8), FSINFO_CAP_NAME(name_has_codepage), FSINFO_CAP_NAME(sparse), FSINFO_CAP_NAME(not_persistent), FSINFO_CAP_NAME(no_unix_mode), FSINFO_CAP_NAME(has_atime), FSINFO_CAP_NAME(has_btime), FSINFO_CAP_NAME(has_ctime), FSINFO_CAP_NAME(has_mtime), }; static void dump_attr_capabilities(union reply *r, int size) { struct fsinfo_capabilities *f = &r->caps; int i; for (i = 0; i < sizeof(f->capabilities); i++) printf("%02x", f->capabilities[i]); printf("\n"); for (i = 0; i < fsinfo_cap__nr; i++) if (f->capabilities[i / 8] & (1 << (i % 8))) printf("\t- %s\n", fsinfo_cap_names[i]); } static void dump_attr_timestamp_info(union reply *r, int size) { struct fsinfo_timestamp_info *f = &r->timestamps; printf("range=%llx-%llx\n", (unsigned long long)f->minimum_timestamp, (unsigned long long)f->maximum_timestamp); #define print_time(G) \ printf("\t"#G"time : gran=%gs\n", \ (f->G##time_gran_mantissa * \ pow(10., f->G##time_gran_exponent))) print_time(a); print_time(b); print_time(c); print_time(m); } static void dump_attr_volume_uuid(union reply *r, int size) { struct fsinfo_volume_uuid *f = &r->uuid; printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x" "-%02x%02x%02x%02x%02x%02x\n", f->uuid[ 0], f->uuid[ 1], f->uuid[ 2], f->uuid[ 3], f->uuid[ 4], f->uuid[ 5], f->uuid[ 6], f->uuid[ 7], f->uuid[ 8], f->uuid[ 9], f->uuid[10], f->uuid[11], f->uuid[12], f->uuid[13], f->uuid[14], f->uuid[15]); } static void dump_attr_server_address(union reply *r, int size) { struct fsinfo_server_address *f = &r->srv_addr; printf("family=%u\n", f->address.ss_family); } static void dump_attr_error_state(union reply *r, int size) { struct fsinfo_error_state *f = &r->errors; printf("io=%u wb=%u bdev=%u\n", f->io_error, f->wb_error, f->bdev_error); } static void dump_attr_io_size(union reply *r, int size) { struct fsinfo_io_size *f = &r->io_size; printf("bs=%u\n", f->block_size); } /* * */ typedef void (*dumper_t)(union reply *r, int size); #define FSINFO_DUMPER(N) [fsinfo_attr_##N] = dump_attr_##N static const dumper_t fsinfo_attr_dumper[fsinfo_attr__nr] = { FSINFO_DUMPER(statfs), FSINFO_DUMPER(fsinfo), FSINFO_DUMPER(ids), FSINFO_DUMPER(limits), FSINFO_DUMPER(supports), FSINFO_DUMPER(capabilities), FSINFO_DUMPER(timestamp_info), FSINFO_DUMPER(volume_uuid), FSINFO_DUMPER(server_address), FSINFO_DUMPER(error_state), FSINFO_DUMPER(io_size), }; static void dump_fsinfo(enum fsinfo_attribute attr, __u8 about, union reply *r, int size) { dumper_t dumper = fsinfo_attr_dumper[attr]; unsigned int len; if (!dumper) { printf("<no dumper>\n"); return; } len = (about & 0x3f) * sizeof(__u32); if (size < len) { printf("<short data %u/%u>\n", size, len); return; } dumper(r, size); } /* * Try one subinstance of an attribute. */ static int try_one(const char *file, struct fsinfo_params *params, bool raw) { union reply r; char *p; int ret; __u8 about; memset(&r.buffer, 0xbd, sizeof(r.buffer)); errno = 0; ret = fsinfo(AT_FDCWD, file, params, r.buffer, sizeof(r.buffer)); if (params->request >= fsinfo_attr__nr) { if (ret == -1 && errno == EOPNOTSUPP) exit(0); fprintf(stderr, "Unexpected error for too-large command %u: %m\n", params->request); exit(1); } //printf("fsinfo(%s,%s,%u,%u) = %d: %m\n", // file, fsinfo_attr_names[params->request], // params->Nth, params->Mth, ret); about = fsinfo_buffer_sizes[params->request]; if (ret == -1) { if (errno == ENODATA) { switch (about & 0xc0) { case 0x00: if (params->Nth == 0 && params->Mth == 0) { fprintf(stderr, "Unexpected ENODATA1 (%u[%u][%u])\n", params->request, params->Nth, params->Mth); exit(1); } break; case 0x40: if (params->Nth == 0 && params->Mth == 0) { fprintf(stderr, "Unexpected ENODATA2 (%u[%u][%u])\n", params->request, params->Nth, params->Mth); exit(1); } break; } return (params->Mth == 0) ? 2 : 1; } if (errno == EOPNOTSUPP) { if (params->Nth > 0 || params->Mth > 0) { fprintf(stderr, "Should return -ENODATA (%u[%u][%u])\n", params->request, params->Nth, params->Mth); exit(1); } //printf("\e[33m%s\e[m: <not supported>\n", // fsinfo_attr_names[attr]); return 2; } perror(file); exit(1); } if (raw) { if (ret > 4096) ret = 4096; dump_hex((unsigned int *)&r.buffer, 0, ret); return 0; } switch (about & 0xc0) { case 0x00: printf("\e[33m%s\e[m: ", fsinfo_attr_names[params->request]); break; case 0x40: printf("\e[33m%s[%u]\e[m: ", fsinfo_attr_names[params->request], params->Nth); break; case 0x80: printf("\e[33m%s[%u][%u]\e[m: ", fsinfo_attr_names[params->request], params->Nth, params->Mth); break; } switch (about) { /* Struct */ case 0x01 ... 0x3f: case 0x41 ... 0x7f: case 0x81 ... 0xbf: dump_fsinfo(params->request, about, &r, ret); return 0; /* String */ case 0x00: case 0x40: case 0x80: if (ret >= 4096) { ret = 4096; r.buffer[4092] = '.'; r.buffer[4093] = '.'; r.buffer[4094] = '.'; r.buffer[4095] = 0; } else { r.buffer[ret] = 0; } for (p = r.buffer; *p; p++) { if (!isprint(*p)) { printf("<non-printable>\n"); continue; } } printf("%s\n", r.buffer); return 0; default: fprintf(stderr, "Fishy about %u %02x\n", params->request, about); exit(1); } } /* * */ int main(int argc, char **argv) { struct fsinfo_params params = { .at_flags = AT_SYMLINK_NOFOLLOW, }; unsigned int attr; int raw = 0, opt, Nth, Mth; while ((opt = getopt(argc, argv, "alr"))) { switch (opt) { case 'a': params.at_flags |= AT_NO_AUTOMOUNT; continue; case 'l': params.at_flags &= ~AT_SYMLINK_NOFOLLOW; continue; case 'r': raw = 1; continue; } break; } argc -= optind; argv += optind; if (argc != 1) { printf("Format: test-fsinfo [-alr] <file>\n"); exit(2); } for (attr = 0; attr <= fsinfo_attr__nr; attr++) { Nth = 0; do { Mth = 0; do { params.request = attr; params.Nth = Nth; params.Mth = Mth; switch (try_one(argv[0], ¶ms, raw)) { case 0: continue; case 1: goto done_M; case 2: goto done_N; } } while (++Mth < 100); done_M: if (Mth >= 100) { fprintf(stderr, "Fishy: Mth == %u\n", Mth); break; } } while (++Nth < 100); done_N: if (Nth >= 100) { fprintf(stderr, "Fishy: Nth == %u\n", Nth); break; } } return 0; }