Filesystem statistics

Updated: October 28, 2024

The most important command that your filesystem should implement is the DCMD_FSYS_STATVFS. In our io_devctl() handler, this ends up calling the utility function cfs_block_fill_statvfs() (in lib/block.c):

void
cfs_block_fill_statvfs (cfs_attr_t *attr, struct __msg_statvfs *r)
{
  uint32_t      nalloc, nfree;
  size_t        nbytes;

  mpool_info (mpool_block, &nbytes, &r -> f_blocks, &nalloc,
              &nfree, NULL, NULL);

  // INVARIANT SECTION

  // file system block size
  r -> f_bsize = nbytes;

  // fundamental filesystem block size
  r -> f_frsize = nbytes;

  // total number of file serial numbers
  r -> f_files = INT_MAX;

  // file system id
  r -> f_fsid = 0x12345678;

  // bit mask of f_flag values
  r -> f_flag = 0;

  // maximum filename length
  r -> f_namemax = NAME_MAX;

  // null terminated name of target file system
  strcpy (r -> f_basetype, "cfs");

  // CALCULATED SECTION

  if (optm) {        // for system-allocated mem with a max

    // tot number of blocks on file system in units of f_frsize
    r -> f_blocks = optm / nbytes;

    // total number of free blocks
    r -> f_bfree = r -> f_blocks - nalloc;

    // total number of free file serial numbers (approximation)
    r -> f_ffree = r -> f_files - nalloc;

  } else if (optM) { // for statically-allocated mem with a max

    // total #blocks on file system in units of f_frsize
    r -> f_blocks = optM / nbytes;

    // total number of free blocks
    r -> f_bfree = nfree;

    // total number of free file serial numbers (approximation)
    r -> f_ffree = nfree;

  } else {           // for unbounded system-allocated memory

    // total #blocks on file system in units of f_frsize
    r -> f_blocks = nalloc + 1;

    // total number of free blocks
    r -> f_bfree = r -> f_blocks - nalloc;

    // total #free file serial numbers (an approximation)
    r -> f_ffree = r -> f_files - nalloc;

  }

  // MIRROR

  // number of free blocks available to non-priv. proc
  r -> f_bavail = r -> f_bfree;

  // number of file serial numbers available to non-priv. proc
  r -> f_favail = r -> f_ffree;
}

The reason for the additional complexity (as opposed to just stuffing the fields directly) is due to the command-line options for the RAM disk. The -m option lets the RAM disk slowly allocate memory for itself as it requires it from the operating system, up to a maximum limit. If you use the -M option instead, the RAM disk allocates the specified memory right up front. Using neither option causes the RAM disk to allocate memory as required, with no limit.

Some of the numbers are outright lies—for example, the f_files value, which is supposed to indicate the total number of file serial numbers, is simply set to INT_MAX. There is no possible way that we would ever use that many file serial numbers (INT_MAX is 9 × 1018)!

So, the job of cfs_block_fill_statvfs() is to gather the information from the block allocator, and stuff the numbers (perhaps calculating some of them) into the struct __msg_statvfs structure.

(QNX Neutrino 7.0 or later) For compatibility between 32- and 64-bit programs, the DCMD_FSYS_STATVFS command uses a __msg_statvfs structure instead of a statvfs structure. The __msg_statvfs structure is equivalent to the 32-bit version of statvfs. The <sys/statvfs.h> header file declares functions and macros that you can use to convert one structure into the other:

/* Conversion methods from/to __msg_statvfs and statvfs */
extern void __msg_statvfs_init64(struct __msg_statvfs * _msg, const struct statvfs64 * _statvfsp);
extern void __msg_statvfs_copy64(struct statvfs64 * _statvfsp, const struct __msg_statvfs * _msg);

/* Alias methods, casting to statvfs64 simplifies handling the fsblkcnt_t high and low bytes */
#define __msg_statvfs_init(_msg, _statvfsp) (__msg_statvfs_init64((_msg), (const struct statvfs64 *)(_statvfsp)))
#define __msg_statvfs_copy(_statvfsp, _msg) (__msg_statvfs_copy64((struct statvfs64 *)(_statvfsp), (_msg)))
  翻译: