#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/resource.h>
#define ARCH64 ((sizeof(long)) > 4)
#define ARCH32 ((sizeof(long)) == 4)
char *testfile = "testlen.dat";
int testfd;
volatile int sigxfsz;
void try(const char *what, int err)
{
if (!err)
return;
fprintf (stderr, "Unexpected result %d. %s: %s\n",
err, what, strerror(errno));
exit(1);
}
void compare(const char *what, int error, int result)
{
if (!error && !result)
return;
if (error && result && error == errno)
return;
if (result && !error)
fprintf (stderr, "%s: unexpected failure, %s\n", what,
strerror(errno));
else if (result)
fprintf (stderr, "%s: wrong failure, %s (expected %s)\n", what,
strerror(errno), strerror(error));
else
fprintf (stderr, "%s: unexpected success\n", what);
exit(1);
}
void check_signal(const char *what, int expected)
{
if (expected == sigxfsz)
return;
if (expected)
fprintf (stderr, "%s: expected signal missing,\n", what);
else
fprintf (stderr, "%s: unexpected signal\n", what);
exit(1);
}
void handle_sigxfsz(int n)
{
sigxfsz = 1;
signal (SIGXFSZ, handle_sigxfsz);
}
void create_file(void)
{
testfd = open(testfile, O_CREAT|O_TRUNC|O_RDWR, 0666);
try ("open(O_CREAT)", testfd < 0);
}
void set_length(long length, int error, int expect_signal)
{
sigxfsz = 0;
compare ("ftruncate", error, ftruncate(testfd, length) != 0);
check_signal ("ftruncate", expect_signal);
}
void test_seek(long where, long expected, int error)
{
errno = 0;
try ("lseek", expected != lseek(testfd, where, SEEK_SET));
compare ("lseek", errno, error);
}
void write_file(long where, long how_much,
long expected, int error, int expect_signal)
{
char *buffer = alloca(how_much);
errno = 0;
sigxfsz = 0;
try ("lseek", where != lseek(testfd, where, SEEK_SET));
try ("write", expected != write(testfd, buffer, how_much));
compare ("write", errno, error);
assert (expect_signal == sigxfsz);
}
/*
* Set the max-file-length resource limit.
*/
void set_maxfile (int max)
{
struct rlimit rlimit;
if (max)
rlimit.rlim_cur = max;
else
rlimit.rlim_cur = RLIM_INFINITY;
rlimit.rlim_max = RLIM_INFINITY;
try ("setrlimit", setrlimit(RLIMIT_FSIZE, &rlimit));
}
int main()
{
long max_filesize;
long soft_limit = 1000000;
signal (SIGXFSZ, handle_sigxfsz);
/* First set of tests. Check out the handling of the filesystem
* offset maximum. */
if (ARCH32)
max_filesize = 0x7FFFFFFFL;
else {
long ind_entries;
struct statfs statbuf;
try ("statfs", statfs (".", &statbuf));
/* We know about ext2 internals here... */
ind_entries = statbuf.f_bsize / 4;
max_filesize = statbuf.f_bsize *
(12L +
ind_entries +
ind_entries * ind_entries +
ind_entries * ind_entries * ind_entries) - 1;
}
printf ("Using a maximum file size of %ld %ld\n", max_filesize);
/* Creating a file and extending to 0 should be a noop. */
create_file();
set_length(0, 0, 0);
printf ("truncate to 0: PASSED\n");
/* Creating a file and extending to maxlen should be fine. */
create_file();
set_length(max_filesize, 0, 0);
printf ("truncate to %ld: PASSED\n", max_filesize);
/* Creating a file 1 beyond maxlen should fail with EINVAL (-ve)
* on ARCH32, and EFBIG (it's still +ve) on ARCH64. */
create_file();
if (ARCH32)
set_length(max_filesize+1, EINVAL, 0);
else
set_length(max_filesize+1, EFBIG, 0);
printf ("truncate to %ld: PASSED\n", max_filesize + 1);
/* Creating a file with -ve size should fail with EINVAL. */
create_file();
set_length(-1, EINVAL, 0);
printf ("truncate to -1: PASSED\n");
/*
* Now test out writes near the limit boundaries.
*/
/* Just test that simple writes work. */
create_file();
set_length(1000, 0, 0);
write_file(1000, 1000, 1000, 0, 0);
printf ("simple write: PASSED\n");
/* Test writes just below maxlen */
create_file();
set_length(max_filesize - 1000, 0, 0);
write_file(max_filesize - 1000, 1000, 1000, 0, 0);
printf ("write (max_filesize-1000, 1000): PASSED\n");
/* Test overwrites just below maxlen */
create_file();
set_length(max_filesize, 0, 0);
write_file(max_filesize - 1000, 1000, 1000, 0, 0);
printf ("overwrite (max_filesize-1000, 1000): PASSED\n");
/* Test writes passing maxlen */
create_file();
set_length(max_filesize - 1000, 0, 0);
write_file(max_filesize - 1000, 1001, 1000, 0, 0);
write_file(max_filesize, 1, -1, EFBIG, 0);
printf ("write (max_filesize-1000, 1001): PASSED\n");
/* Test writes at maxlen */
create_file();
write_file(max_filesize, 1000, -1, EFBIG, 0);
printf ("write (max_filesize, 1000): PASSED\n");
/* Test writes beyond maxlen */
create_file();
test_seek(max_filesize+1000, -1, EINVAL);
printf ("seek (max_filesize+1000): PASSED\n");
/*
* Set up a filesize resource limit and repeat the tests.
* Truncate first:
*/
set_maxfile(soft_limit);
printf ("Established soft file size limit.\n");
/* Creating a file and extending to 0 should be a noop. */
create_file();
set_length(0, 0, 0);
printf ("truncate to 0: PASSED\n");
/* Creating a file and extending to the soft limit should be
fine. */
create_file();
set_length(soft_limit, 0, 0);
printf ("truncate to soft_limit: PASSED\n");
/* Creating a file 1 beyond the soft limit should fail with
* EFBIG and SIGXFSZ. */
create_file();
set_length(soft_limit+1, EFBIG, 1);
printf ("truncate to soft_limit+1: PASSED\n");
/* Creating a file with -ve size should fail with EINVAL. */
create_file();
set_length(-1, EINVAL, 0);
printf ("truncate to -1: PASSED\n");
/*
* Now the write tests again.
*/
/* Just test that simple writes work. */
create_file();
set_length(1000, 0, 0);
write_file(1000, 1000, 1000, 0, 0);
printf ("simple write: PASSED\n");
/* Test writes just below soft_limit */
create_file();
set_length(soft_limit - 1000, 0, 0);
write_file(soft_limit - 1000, 1000, 1000, 0, 0);
printf ("write (soft_limit-1000, 1000): PASSED\n");
/* Test overwrites just below soft_limit */
create_file();
set_length(soft_limit, 0, 0);
write_file(soft_limit - 1000, 1000, 1000, 0, 0);
printf ("overwrite (soft_limit-1000, 1000): PASSED\n");
/* Test writes passing soft_limit */
create_file();
set_length(soft_limit - 1000, 0, 0);
write_file(soft_limit - 1000, 1001, 1000, 0, 0);
write_file(soft_limit, 1, -1, EFBIG, 1);
printf ("write (soft_limit-1000, 1001): PASSED\n");
/* Test writes at soft_limit */
create_file();
write_file(soft_limit, 1000, -1, EFBIG, 1);
printf ("write (soft_limit, 1000): PASSED\n");
/* Test writes beyond soft_limit */
create_file();
test_seek(soft_limit+1000, soft_limit+1000, 0);
write_file(soft_limit+1000, 1000, -1, EFBIG, 1);
printf ("seek (soft_limit+1000): PASSED\n");
return 0;
}