blob: f1f800ef92a8b7e4075f92f32bcabb01d06a590d [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
#include "Charmonizer/Core/HeaderChecker.h"
#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/Stat.h"
#include "Charmonizer/Core/Util.h"
#include "Charmonizer/Probe/LargeFiles.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* Sets of symbols which might provide large file support. */
typedef struct off64_combo {
const char *includes;
const char *fopen_command;
const char *ftell_command;
const char *fseek_command;
const char *offset64_type;
} off64_combo;
static off64_combo off64_combos[] = {
{ "#include <sys/types.h>\n", "fopen64", "ftello64", "fseeko64", "off64_t" },
{ "#include <sys/types.h>\n", "fopen", "ftello64", "fseeko64", "off64_t" },
{ "#include <sys/types.h>\n", "fopen", "ftello", "fseeko", "off_t" },
{ "", "fopen", "ftell", "fseek", "off_t" },
{ "", "fopen", "_ftelli64", "_fseeki64", "__int64" },
{ "", "fopen", "ftell", "fseek", "long" },
{ NULL, NULL, NULL, NULL, NULL }
};
typedef struct unbuff_combo {
const char *includes;
const char *lseek_command;
const char *pread64_command;
} unbuff_combo;
static unbuff_combo unbuff_combos[] = {
{ "#include <unistd.h>\n#include <fcntl.h>\n", "lseek64", "pread64" },
{ "#include <unistd.h>\n#include <fcntl.h>\n", "lseek", "pread" },
{ "#include <io.h>\n#include <fcntl.h>\n", "_lseeki64", "NO_PREAD64" },
{ NULL, NULL, NULL }
};
/* Check what name 64-bit ftell, fseek go by.
*/
static chaz_bool_t
S_probe_off64(off64_combo *combo);
/* Check for a 64-bit lseek.
*/
static chaz_bool_t
S_probe_lseek(unbuff_combo *combo);
/* Check for a 64-bit pread.
*/
static chaz_bool_t
S_probe_pread64(unbuff_combo *combo);
/* Determine whether we can use sparse files.
*/
static chaz_bool_t
S_check_sparse_files(void);
/* Helper for check_sparse_files().
*/
static void
S_test_sparse_file(long offset, 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 chaz_bool_t
S_can_create_big_files(void);
/* Vars for holding lfs commands, once they're discovered. */
static char fopen_command[10];
static char fseek_command[10];
static char ftell_command[10];
static char lseek_command[10];
static char pread64_command[10];
static char off64_type[10];
void
LargeFiles_run(void) {
chaz_bool_t success = false;
chaz_bool_t found_lseek = false;
chaz_bool_t found_pread64 = false;
unsigned i;
ConfWriter_start_module("LargeFiles");
/* See if off64_t and friends exist or have synonyms. */
for (i = 0; off64_combos[i].includes != NULL; i++) {
off64_combo combo = off64_combos[i];
success = S_probe_off64(&combo);
if (success) {
strcpy(fopen_command, combo.fopen_command);
strcpy(fseek_command, combo.fseek_command);
strcpy(ftell_command, combo.ftell_command);
strcpy(off64_type, combo.offset64_type);
break;
}
}
/* Write the affirmations/definitions. */
if (success) {
ConfWriter_append_conf("#define CHY_HAS_LARGE_FILE_SUPPORT\n");
/* Alias these only if they're not already provided and correct. */
if (strcmp(off64_type, "off64_t") != 0) {
ConfWriter_append_conf("#define chy_off64_t %s\n", off64_type);
ConfWriter_append_conf("#define chy_fopen64 %s\n", fopen_command);
ConfWriter_append_conf("#define chy_ftello64 %s\n", ftell_command);
ConfWriter_append_conf("#define chy_fseeko64 %s\n", fseek_command);
}
}
/* Probe for 64-bit versions of lseek and pread (if we have an off64_t). */
if (success) {
for (i = 0; unbuff_combos[i].lseek_command != NULL; i++) {
unbuff_combo combo = unbuff_combos[i];
found_lseek = S_probe_lseek(&combo);
if (found_lseek) {
strcpy(lseek_command, combo.lseek_command);
ConfWriter_append_conf("#define chy_lseek64 %s\n",
lseek_command);
break;
}
}
for (i = 0; unbuff_combos[i].pread64_command != NULL; i++) {
unbuff_combo combo = unbuff_combos[i];
found_pread64 = S_probe_pread64(&combo);
if (found_pread64) {
strcpy(pread64_command, combo.pread64_command);
ConfWriter_append_conf("#define chy_pread64 %s\n",
pread64_command);
found_pread64 = true;
break;
}
}
}
/* Check for sparse files. */
if (S_check_sparse_files()) {
ConfWriter_append_conf("#define CHAZ_HAS_SPARSE_FILES\n");
/* See if we can create a 5 GB file without crashing. */
if (success && S_can_create_big_files()) {
ConfWriter_append_conf("#define CHAZ_CAN_CREATE_BIG_FILES\n");
}
}
else {
ConfWriter_append_conf("#define CHAZ_NO_SPARSE_FILES\n");
}
/* Short names. */
if (success) {
ConfWriter_start_short_names();
ConfWriter_shorten_macro("HAS_LARGE_FILE_SUPPORT");
/* Alias these only if they're not already provided and correct. */
if (strcmp(off64_type, "off64_t") != 0) {
ConfWriter_shorten_typedef("off64_t");
ConfWriter_shorten_function("fopen64");
ConfWriter_shorten_function("ftello64");
ConfWriter_shorten_function("fseeko64");
}
if (found_lseek && strcmp(lseek_command, "lseek64") != 0) {
ConfWriter_shorten_function("lseek64");
}
if (found_pread64 && strcmp(pread64_command, "pread64") != 0) {
ConfWriter_shorten_function("pread64");
}
ConfWriter_end_short_names();
}
ConfWriter_end_module();
}
/* Code for checking ftello64 and friends. */
static const char off64_code[] =
QUOTE( %s )
QUOTE( #include "_charm.h" )
QUOTE( int main() { )
QUOTE( %s pos; )
QUOTE( FILE *f; )
QUOTE( Charm_Setup; )
QUOTE( f = %s("_charm_off64", "w"); )
QUOTE( if (f == NULL) return -1; )
QUOTE( printf("%%d", (int)sizeof(%s)); )
QUOTE( pos = %s(stdout); )
QUOTE( %s(stdout, 0, SEEK_SET); )
QUOTE( return 0; )
QUOTE( } );
static chaz_bool_t
S_probe_off64(off64_combo *combo) {
char *output = NULL;
size_t output_len;
size_t needed = sizeof(off64_code)
+ (2 * strlen(combo->offset64_type))
+ strlen(combo->fopen_command)
+ strlen(combo->ftell_command)
+ strlen(combo->fseek_command)
+ 20;
char *code_buf = (char*)malloc(needed);
chaz_bool_t success = false;
/* Prepare the source code. */
sprintf(code_buf, off64_code, combo->includes, combo->offset64_type,
combo->fopen_command, combo->offset64_type, combo->ftell_command,
combo->fseek_command);
/* Verify compilation and that the offset type has 8 bytes. */
output = CC_capture_output(code_buf, strlen(code_buf), &output_len);
if (output != NULL) {
long size = strtol(output, NULL, 10);
if (size == 8) {
success = true;
}
free(output);
}
if (!Util_remove_and_verify("_charm_off64")) {
Util_die("Failed to remove '_charm_off64'");
}
return success;
}
/* Code for checking 64-bit lseek. */
static const char lseek_code[] =
QUOTE( %s )
QUOTE( #include "_charm.h" )
QUOTE( int main() { )
QUOTE( int fd; )
QUOTE( Charm_Setup; )
QUOTE( fd = open("_charm_lseek", O_WRONLY | O_CREAT, 0666); )
QUOTE( if (fd == -1) { return -1; } )
QUOTE( %s(fd, 0, SEEK_SET); )
QUOTE( printf("%%d", 1); )
QUOTE( if (close(fd)) { return -1; } )
QUOTE( return 0; )
QUOTE( } );
static chaz_bool_t
S_probe_lseek(unbuff_combo *combo) {
char *output = NULL;
size_t output_len;
size_t needed = sizeof(lseek_code)
+ strlen(combo->includes)
+ strlen(combo->lseek_command)
+ 20;
char *code_buf = (char*)malloc(needed);
chaz_bool_t success = false;
/* Verify compilation. */
sprintf(code_buf, lseek_code, combo->includes, combo->lseek_command);
output = CC_capture_output(code_buf, strlen(code_buf), &output_len);
if (output != NULL) {
success = true;
free(output);
}
if (!Util_remove_and_verify("_charm_lseek")) {
Util_die("Failed to remove '_charm_lseek'");
}
free(code_buf);
return success;
}
/* Code for checking 64-bit pread. The pread call will fail, but that's fine
* as long as it compiles. */
static const char pread64_code[] =
QUOTE( %s )
QUOTE( #include "_charm.h" )
QUOTE( int main() { )
QUOTE( int fd = 20; )
QUOTE( char buf[1]; )
QUOTE( Charm_Setup; )
QUOTE( printf("1"); )
QUOTE( %s(fd, buf, 1, 1); )
QUOTE( return 0; )
QUOTE( } );
static chaz_bool_t
S_probe_pread64(unbuff_combo *combo) {
char *output = NULL;
size_t output_len;
size_t needed = sizeof(pread64_code)
+ strlen(combo->includes)
+ strlen(combo->pread64_command)
+ 20;
char *code_buf = (char*)malloc(needed);
chaz_bool_t success = false;
/* Verify compilation. */
sprintf(code_buf, pread64_code, combo->includes, combo->pread64_command);
output = CC_capture_output(code_buf, strlen(code_buf), &output_len);
if (output != NULL) {
success = true;
free(output);
}
free(code_buf);
return success;
}
static chaz_bool_t
S_check_sparse_files(void) {
Stat st_a, st_b;
/* Bail out if we can't stat() a file. */
if (!HeadCheck_check_header("sys/stat.h")) {
return false;
}
/* Write and stat a 1 MB file and a 2 MB file, both of them sparse. */
S_test_sparse_file(1000000, &st_a);
S_test_sparse_file(2000000, &st_b);
if (!(st_a.valid && st_b.valid)) {
return false;
}
if (st_a.size != 1000001) {
Util_die("Expected size of 1000001 but got %ld", (long)st_a.size);
}
if (st_b.size != 2000001) {
Util_die("Expected size of 2000001 but got %ld", (long)st_b.size);
}
/* See if two files with very different lengths have the same block size. */
if (st_a.blocks == st_b.blocks) {
return true;
}
else {
return false;
}
}
static void
S_test_sparse_file(long offset, Stat *st) {
FILE *sparse_fh;
/* Make sure the file's not there, then open. */
Util_remove_and_verify("_charm_sparse");
if ((sparse_fh = fopen("_charm_sparse", "w+")) == NULL) {
Util_die("Couldn't open file '_charm_sparse'");
}
/* Seek fh to [offset], write a byte, close file. */
if ((fseek(sparse_fh, offset, SEEK_SET)) == -1) {
Util_die("seek failed: %s", strerror(errno));
}
if ((fprintf(sparse_fh, "X")) != 1) {
Util_die("fprintf failed");
}
if (fclose(sparse_fh)) {
Util_die("Error closing file '_charm_sparse': %s", strerror(errno));
}
/* Stat the file. */
Stat_stat("_charm_sparse", st);
remove("_charm_sparse");
}
/* Open a file, seek to a loc, print a char, and communicate success. */
static const char create_bigfile_code[] =
QUOTE( #include "_charm.h" )
QUOTE( int main() { )
QUOTE( FILE *fh = fopen("_charm_large_file_test", "w+"); )
QUOTE( int check_seek; )
QUOTE( Charm_Setup; )
/* Bail unless seek succeeds. */
QUOTE( check_seek = %s(fh, 5000000000, SEEK_SET); )
QUOTE( if (check_seek == -1) )
QUOTE( exit(1); )
/* Bail unless we write successfully. */
QUOTE( if (fprintf(fh, "X") != 1) )
QUOTE( exit(1); )
QUOTE( if (fclose(fh)) )
QUOTE( exit(1); )
/* Communicate success to Charmonizer. */
QUOTE( printf("1"); )
QUOTE( return 0; )
QUOTE( } );
static chaz_bool_t
S_can_create_big_files(void) {
char *output;
size_t output_len;
FILE *truncating_fh;
size_t needed = strlen(create_bigfile_code)
+ strlen(fseek_command)
+ 10;
char *code_buf = (char*)malloc(needed);
/* Concat the source strings, compile the file, capture output. */
sprintf(code_buf, create_bigfile_code, fseek_command);
output = CC_capture_output(code_buf, strlen(code_buf), &output_len);
/* Truncate, just in case the call to remove fails. */
truncating_fh = fopen("_charm_large_file_test", "w");
if (truncating_fh != NULL) {
fclose(truncating_fh);
}
Util_remove_and_verify("_charm_large_file_test");
/* Return true if the test app made it to the finish line. */
free(code_buf);
return output == NULL ? false : true;
}