From: Volker Lendecke Date: Wed, 23 Aug 2017 10:48:03 +0000 (+0200) Subject: tdb: Truncate the file after expand failure X-Git-Tag: tdb-1.3.15~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c7211882a79;p=thirdparty%2Fsamba.git tdb: Truncate the file after expand failure Without this it's very easy to create virtually huge files: ftruncate expands a file, the pwrites fail with ENOSPC, thus the write fails. The next writer runs into the same situation, and ftruncate-expands the file even further. tdb_check will then spend ages reading the 4GB of zeros byte by byte. Here we hold the freelist lock or are inside a transaction, so it is safe to cut the file again. Nobody can have used the space that we have tried to allocate, so we can't have any stray pointers corrupting the database. Signed-off-by: Volker Lendecke Reviewed-by: Andrew Bartlett --- diff --git a/lib/tdb/common/io.c b/lib/tdb/common/io.c index 1b422affdca..f7a12c34dc9 100644 --- a/lib/tdb/common/io.c +++ b/lib/tdb/common/io.c @@ -430,14 +430,14 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write " "returned 0 twice: giving up!\n")); errno = ENOSPC; - return -1; + goto fail; } if (written == -1) { tdb->ecode = TDB_ERR_OOM; TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of " "%u bytes failed (%s)\n", (int)n, strerror(errno))); - return -1; + goto fail; } if (written != n) { TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote " @@ -448,6 +448,29 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad size += written; } return 0; + +fail: + { + int err = errno; + int ret; + + /* + * We're holding the freelist lock or are inside a + * transaction. Cutting the file is safe, the space we + * tried to allocate can't have been used anywhere in + * the meantime. + */ + + ret = tdb_ftruncate(tdb, size); + if (ret == -1) { + TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: " + "retruncate to %ju failed\n", + (uintmax_t)size)); + } + errno = err; + } + + return -1; }