blob: c914e85845d82aaede0f5502565c578d876307c9 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define CHAZ_USE_SHORT_NAMES
#define CHY_EMPLOY_INTEGERLITERALS
#include "charmony.h"
#include <string.h>
#include <errno.h>
#ifdef CHAZ_HAS_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef CHAZ_HAS_FCNTL_H
#include <fcntl.h> /* open, fcntl flags */
#endif
#ifdef CHY_HAS_UNISTD_H
#include <unistd.h> /* close */
#endif
#ifdef CHAZ_HAS_IO_H
#include <io.h>
#endif
#include <stdio.h>
#include "Charmonizer/Test.h"
#if (defined(HAS_64BIT_STDIO) \
&& defined(CHAZ_HAS_STAT_ST_SIZE) \
&& defined(CHAZ_HAS_STAT_ST_BLOCKS))
#define STAT_TESTS_ENABLED
#include <sys/stat.h>
/* Determine whether we can use sparse files.
*/
static int
S_check_sparse_files(void);
/* Helper for check_sparse_files().
*/
static int
S_test_sparse_file(long offset, struct stat *st);
/* See if trying to write a 5 GB file in a subprocess bombs out. If it
* doesn't, then the test suite can safely verify large file support.
*/
static int
S_can_create_big_files(void);
#endif /* criteria for defining STAT_TESTS_ENABLED */
#ifdef O_LARGEFILE
#define LARGEFILE_OPEN_FLAG O_LARGEFILE
#else
#define LARGEFILE_OPEN_FLAG 0
#endif
static void
S_run_tests(void) {
FILE *fh;
off64_t offset;
int check_val;
char check_char;
int fd;
/* A little over 4 GB, and a little over 2 GB. */
off64_t gb4_plus = ((off64_t)0x7FFFFFFF << 1) + 100;
off64_t gb2_plus = (off64_t)0x7FFFFFFF + 200;
LONG_EQ(sizeof(off64_t), 8, "off64_t type has 8 bytes");
#ifndef HAS_64BIT_STDIO
SKIP_REMAINING("No stdio large file support");
return;
#endif
#ifndef STAT_TESTS_ENABLED
SKIP_REMAINING("Need stat with st_size and st_blocks");
return;
#else
/* Check for sparse files. */
if (!S_check_sparse_files()) {
SKIP_REMAINING("Can't verify large file support "
"without sparse files");
return;
}
if (!S_can_create_big_files()) {
SKIP_REMAINING("Unsafe to create 5GB sparse files on this system");
return;
}
fh = fopen64("_charm_large_file_test", "w+");
if (fh == NULL) {
SKIP_REMAINING("Failed to open file");
return;
}
check_val = fseeko64(fh, gb4_plus, SEEK_SET);
LONG_EQ(check_val, 0, "fseeko64 above 4 GB");
offset = ftello64(fh);
OK((offset == gb4_plus), "ftello64 above 4 GB");
check_val = fprintf(fh, "X");
LONG_EQ(check_val, 1, "print above 4 GB");
check_val = fseeko64(fh, gb2_plus, SEEK_SET);
LONG_EQ(check_val, 0, "fseeko64 above 2 GB");
offset = ftello64(fh);
OK((offset == gb2_plus), "ftello64 above 2 GB");
check_val = fseeko64(fh, -1, SEEK_END);
LONG_EQ(check_val, 0, "seek to near end");
check_char = fgetc(fh);
LONG_EQ(check_char, 'X', "read value after multiple seeks");
check_val = fclose(fh);
LONG_EQ(check_val, 0, "fclose succeeds after all that");
/* Truncate, just in case the call to remove fails. */
fh = fopen64("_charm_large_file_test", "w+");
if (fh != NULL) {
fclose(fh);
}
remove("_charm_large_file_test");
#ifndef HAS_64BIT_LSEEK
SKIP_REMAINING("No 64-bit lseek");
return;
#else
fd = open("_charm_large_file_test",
O_RDWR | O_CREAT | LARGEFILE_OPEN_FLAG, 0666);
if (fd == -1) {
FAIL("open failed");
SKIP_REMAINING("open failed");
return;
}
offset = lseek64(fd, gb4_plus, SEEK_SET);
OK(offset == gb4_plus, "lseek64 above 4 GB");
offset = lseek64(fd, 0, SEEK_CUR);
OK(offset == gb4_plus, "lseek64 in place above 4 GB");
check_val = write(fd, "X", 1);
LONG_EQ(check_val, 1, "write() above 4 GB");
offset = lseek64(fd, gb2_plus, SEEK_SET);
OK(offset == gb2_plus, "lseek64 above 2 GB");
offset = lseek64(fd, 0, SEEK_CUR);
OK((offset == gb2_plus), "lseek64 in place above 2 GB");
offset = lseek64(fd, -1, SEEK_END);
OK(offset == gb4_plus, "seek to near end");
check_val = read(fd, &check_char, 1);
LONG_EQ(check_val, 1, "read() after multiple lseek64 calls");
LONG_EQ(check_char, 'X',
"read() correct data after multiple lseek64 calls");
#ifdef HAS_64BIT_PREAD
check_char = 0;
check_val = pread64(fd, &check_char, 1, gb4_plus);
LONG_EQ(check_val, 1, "pread64");
LONG_EQ(check_char, 'X', "pread64() correct data");
#else
SKIP("no pread64");
SKIP("no pread64");
#endif
check_val = close(fd);
LONG_EQ(check_val, 0, "close succeeds after all that");
#endif
/* Truncate, just in case the call to remove fails. */
fh = fopen64("_charm_large_file_test", "w+");
if (fh != NULL) {
fclose(fh);
}
remove("_charm_large_file_test");
#endif /* STAT_TESTS_ENABLED */
}
#ifdef STAT_TESTS_ENABLED
static int
S_check_sparse_files(void) {
struct stat st_a, st_b;
/* Write and stat a 1 MB file and a 2 MB file, both of them sparse. */
if (!S_test_sparse_file(1000000, &st_a)) { return false; }
if (!S_test_sparse_file(2000000, &st_b)) { return false; }
if (st_a.st_size != 1000001 || st_b.st_size != 2000001) {
return false;
}
/* See if two files with very different lengths have the same block size. */
return st_a.st_blocks == st_b.st_blocks ? true : false;
}
static int
S_test_sparse_file(long offset, struct stat *st) {
FILE *sparse_fh;
int result = false;
/* Make sure the file's not there, then open. */
remove("_charm_sparse");
if ((sparse_fh = fopen64("_charm_sparse", "w+")) == NULL) {
return false;
}
/* Seek fh to [offset], write a byte, close file. */
if ((fseeko64(sparse_fh, offset, SEEK_SET)) != -1) {
if ((fprintf(sparse_fh, "X")) == 1) {
result = true;
}
}
if (fclose(sparse_fh)) {
result = false;
}
/* Stat the file. */
stat("_charm_sparse", st);
remove("_charm_sparse");
return result;
}
static int
S_can_create_big_files(void) {
int result = 0;
FILE *fh = fopen64("_charm_large_file_test", "w+");
if (!fh) {
return false;
}
else {
/* Bail unless seek succeeds. */
int64_t check_seek = fseeko64(fh, INT64_C(5000000000), SEEK_SET);
if (check_seek != -1) {
/* Bail unless we write successfully. */
if (fprintf(fh, "X") == 1) {
result = true;
}
}
if (fclose(fh)) {
result = false;
}
}
/* Truncate, just in case the call to remove fails. */
fh = fopen64("_charm_large_file_test", "w");
if (fh != NULL) {
fclose(fh);
}
remove("_charm_large_file_test");
return result;
}
#endif /* STAT_TESTS_ENABLED */
int
chaz_Test_test_large_files() {
Test_start(20);
S_run_tests();
return Test_finish();
}