Hi there,

ext2fs currently has #if 0 around the size check for lengthening a
file. This allows ftruncate(2) calls for very large files to succeed,
but result in a smaller file than was requested. I noticed the problem
while trying to create vmd(8) images on an ext2fs filesystem.

Below is a patch that fixes the problem, followed by a program that
demonstrates the problem.

I am not certain this patch is correct. A bit further down in
ext2fs_inode.c, we find:

        error = ext2fs_buf_alloc(oip, lbn, offset + 1, cred, &bp,
            aflags);
        if (error)
                return (error);
        (void)ext2fs_setsize(oip, length);

ext2fs_setsize will already return EFBIG if the requested size is too
large, and we then throw this information away with a void cast.
However, I am not sure how to properly clean up the effects of
ext2fs_buf_alloc in case of failure this late; it may be simpler to
perform the check earlier (as in the patch below).

I am happy to work on a better diff if this failure should be handled
differently.


Index: ext2fs_inode.c
===================================================================
RCS file: /cvs/src/sys/ufs/ext2fs/ext2fs_inode.c,v
retrieving revision 1.58
diff -u -p -r1.58 ext2fs_inode.c
--- ext2fs_inode.c      19 Mar 2016 12:04:16 -0000      1.58
+++ ext2fs_inode.c      27 May 2017 19:11:48 -0000
@@ -250,10 +250,8 @@ ext2fs_truncate(struct inode *oip, off_t
         * value of osize is 0, length will be at least 1.
         */
        if (osize < length) {
-#if 0 /* XXX */
-               if (length > fs->fs_maxfilesize)
+               if (length > fs->e2fs_maxfilesize)
                        return (EFBIG);
-#endif
                offset = blkoff(fs, length - 1);
                lbn = lblkno(fs, length - 1);
                aflags = B_CLRBUF;


ftruncate.c
===================================================================
/*
 * This program demonstrates a bug in OpenBSD's ext2fs implementation.
 * The bug causes ftruncate(2) on ext2fs not to perform size checks
 * prior to lengthening a file, so that inconsistent behaviour results
 * if a file is ftruncate(2)'d beyond the maximum supported file size.
 *
 * The program is to be run with fd 3 directed to the file that should
 * be truncated.
 *
 * Expected behaviour with a maximum file size of 2 TiB minus one byte:
 *
 *   $ ./ftruncate 3>foo
 *   ftruncate: ftruncate 1T: success, file size is 1.0T
 *   ftruncate: ftruncate 2T: File too large
 *   ftruncate: ftruncate 4T: File too large
 *   ftruncate: ftruncate 9T: File too large
 *   ftruncate: ftruncate 10T: File too large
 *   ftruncate: ftruncate 100T: File too large
 *   ftruncate: ftruncate 900T: File too large
 *
 * Actual behaviour with the bug:
 *
 *   $ ./ftruncate 3>foo
 *   ftruncate: ftruncate 1T: success, file size is 1.0T
 *   ftruncate: ftruncate 2T: success, file size is 1.0T
 *   ftruncate: ftruncate 4T: success, file size is 1.0T
 *   ftruncate: ftruncate 9T: File too large
 *   ftruncate: ftruncate 10T: File too large
 *   ftruncate: ftruncate 100T: success, file size is 1.0T
 *   ftruncate: ftruncate 900T: success, file size is 1.0T
 */

#include <sys/stat.h>

#include <err.h>
#include <unistd.h>
#include <util.h>

#define FD 3

void do_ftruncate(char*);

int
main() {
        do_ftruncate("1T");
        do_ftruncate("2T");
        do_ftruncate("4T");
        do_ftruncate("9T");
        do_ftruncate("10T");
        do_ftruncate("100T");
        do_ftruncate("900T");
        return 0;
}

void
do_ftruncate(char* size) {
        long long r;
        struct stat sb;
        char rsize[FMT_SCALED_STRSIZE];

        if (scan_scaled(size, &r) != 0)
                err(1, "scan_scaled %s", size);

        if (ftruncate(FD, r) != 0) {
                warn("ftruncate %s", size);
                return;
        }

        if (fstat(3, &sb) != 0)
                err(1, "fstat");

        if (fmt_scaled(sb.st_size, rsize) != 0)
                err(1, "fmt_scaled %lld", sb.st_size);

        warnx("ftruncate %s: success, file size is %s", size, rsize);
}

Reply via email to