An issue was found with the netBeans installer where
the installation was failing on a large ZFS filesystem.

This resulted in CR 6560644 (zfs statvfs f_frsize needs work).
The issue is that large filesystems can cause EOVERFLOW on
statvfs() calls. This behavior is documented in the statvfs(2)
man page, but I think we can do better.

The problem was initially reported against ZFS, and my first
fix was to zfs_statvfs().  But the VFS_STATVFS routines only
see a statvfs64, so they have no way to tell if they're
about to cause EOVERFLOW.  This means that they would always
have to limit themselves to 32-bit values, which seems
like the wrong direction for a fix.

The change detects when cstatvfs32 is about to return EOVERFLOW,
and attempts to rescale the statvfs values so that we return valid
(albeit less precise) information.

Please send me any comments or suggestions, especially
with respect to conformance.

-Chris


Here's the suggested patch:

--- old/usr/src/uts/common/syscall/statvfs.c    Mon Aug 27 11:17:30 2007
+++ new/usr/src/uts/common/syscall/statvfs.c    Mon Aug 27 11:17:30 2007
@@ -19,7 +19,7 @@
   * CDDL HEADER END
   */
  /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */

@@ -31,7 +31,7 @@
   * under license from the Regents of the University of California.
   */

-#pragma ident  "@(#)statvfs.c  1.19    06/05/17 SMI"
+#pragma ident  "@(#)statvfs.c  1.20    07/08/27 SMI"

  /*
   * Get file system statistics (statvfs and fstatvfs).
@@ -76,6 +76,27 @@
   * Common routines for statvfs and fstatvfs.
   */

+/*
+ * Underlying VFS_STATVFS routines can't tell if they are going to cause
+ * EOVERFLOW with f_frsize, because all they see is a struct statvfs64.
+ * If EOVERFLOW was about to occur, try to adjust f_frsize upward so that
+ * we return valid (but less precise) block information.
+ */
+static void
+rescale_frsize(struct statvfs64 *sbp)
+{
+       /*
+        * We're limited to 32 bits for the number of frsize blocks.
+        * Find the smallest power of 2 that won't cause EOVERFLOW.
+        */
+       while (sbp->f_blocks > UINT32_MAX && sbp->f_frsize < (1U << 31)) {
+               sbp->f_frsize <<= 1;
+               sbp->f_blocks >>= 1;
+               sbp->f_bfree >>= 1;
+               sbp->f_bavail >>= 1;
+       }
+}
+
  static int
  cstatvfs32(struct vfs *vfsp, struct statvfs32 *ubp)
  {
@@ -114,10 +135,28 @@
        if (ds64.f_bfree == (fsblkcnt64_t)-1)
                ds64.f_bfree = UINT32_MAX;

+       /*
+        * If we're about to cause EOVERFLOW with any of the inode
+        * counts, cap the value(s) at UINT32_MAX.
+        */
+       if (ds64.f_files > UINT32_MAX)
+               ds64.f_files = UINT32_MAX;
+       if (ds64.f_ffree > UINT32_MAX)
+               ds64.f_ffree = UINT32_MAX;
+       if (ds64.f_favail > UINT32_MAX)
+               ds64.f_favail = UINT32_MAX;
+
+       /*
+        * If necessary, try to avoid EOVERFLOW by increasing f_frsize.
+        */
        if (ds64.f_blocks > UINT32_MAX || ds64.f_bfree > UINT32_MAX ||
-           ds64.f_bavail > UINT32_MAX || ds64.f_files > UINT32_MAX ||
-           ds64.f_ffree > UINT32_MAX || ds64.f_favail > UINT32_MAX)
+           ds64.f_bavail > UINT32_MAX)
+               rescale_frsize(&ds64);
+
+       if (ds64.f_blocks > UINT32_MAX || ds64.f_bfree > UINT32_MAX ||
+           ds64.f_bavail > UINT32_MAX)
                return (EOVERFLOW);
+
  #ifdef _LP64
        /*
         * On the 64-bit kernel, even these fields grow to 64-bit


Reply via email to