ffs_dirpref() calculates dirsize = (fs->fs_avgfilesize * fs->fs_avgfpdir).
authorMatthew Dillon <dillon@dragonflybsd.org>
Mon, 9 Aug 2004 19:41:04 +0000 (19:41 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Mon, 9 Aug 2004 19:41:04 +0000 (19:41 +0000)
However, these two paramters can be set by the sysop such that they easily
overflow a 32 bit integer.  Use 64 bit arithmatic to prevent the overflow
and special-case values > 0x7fffffff to simply set maxcontigdirs to 1 rather
then waste time doing further 64 bit arithmatic.

Note that NetBSD also changed 'cgsize' to a 64 bit integer, but fails to
properly cast at least one of the arguments in both the dirsize and cgsize
multiplication which means that the overflow bug in NetBSD has not actually
been fixed.

This patch does not change the cgsize calculation yet, it requires more
review.

Source: NetBSD ffs_alloc.c/1.74 from Barry Bouwsma of Tiengen.

sys/vfs/ufs/ffs_alloc.c

index 57349cc..64edc14 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)ffs_alloc.c 8.18 (Berkeley) 5/26/95
  * $FreeBSD: src/sys/ufs/ffs/ffs_alloc.c,v 1.64.2.2 2001/09/21 19:15:21 dillon Exp $
- * $DragonFly: src/sys/vfs/ufs/ffs_alloc.c,v 1.10 2004/07/18 19:43:48 drhodus Exp $
+ * $DragonFly: src/sys/vfs/ufs/ffs_alloc.c,v 1.11 2004/08/09 19:41:04 dillon Exp $
  */
 
 #include "opt_quota.h"
@@ -642,6 +642,7 @@ ffs_dirpref(struct inode *pip)
 {
        struct fs *fs;
        int cg, prefcg, dirsize, cgsize;
+       int64_t dirsize64;
        int avgifree, avgbfree, avgndir, curdirsize;
        int minifree, minbfree, maxndir;
        int mincg, minndir;
@@ -689,16 +690,27 @@ ffs_dirpref(struct inode *pip)
        if (minbfree < 1)
                minbfree = 1;
        cgsize = fs->fs_fsize * fs->fs_fpg;
-       dirsize = fs->fs_avgfilesize * fs->fs_avgfpdir;
-       curdirsize = avgndir ? (cgsize - avgbfree * fs->fs_bsize) / avgndir : 0;
-       if (dirsize < curdirsize)
-               dirsize = curdirsize;
-       maxcontigdirs = min((avgbfree * fs->fs_bsize) / dirsize, 255);
-       if (fs->fs_avgfpdir > 0)
-               maxcontigdirs = min(maxcontigdirs,
-                                   fs->fs_ipg / fs->fs_avgfpdir);
-       if (maxcontigdirs == 0)
+
+       /*
+        * fs_avgfilesize and fs_avgfpdir are user-settable entities and
+        * multiplying them may overflow a 32 bit integer.
+        */
+       dirsize64 = fs->fs_avgfilesize * (int64_t)fs->fs_avgfpdir;
+       if (dirsize64 > 0x7fffffff) {
                maxcontigdirs = 1;
+       } else {
+               dirsize = (int)dirsize64;
+               curdirsize = avgndir ?
+                       (cgsize - avgbfree * fs->fs_bsize) / avgndir : 0;
+               if (dirsize < curdirsize)
+                       dirsize = curdirsize;
+               maxcontigdirs = min((avgbfree * fs->fs_bsize) / dirsize, 255);
+               if (fs->fs_avgfpdir > 0)
+                       maxcontigdirs = min(maxcontigdirs,
+                                   fs->fs_ipg / fs->fs_avgfpdir);
+               if (maxcontigdirs == 0)
+                       maxcontigdirs = 1;
+       }
 
        /*
         * Limit number of dirs in one cg and reserve space for