diff --git a/README.adoc b/README.adoc index 295995c..1b70be8 100644 --- a/README.adoc +++ b/README.adoc @@ -17108,6 +17108,7 @@ Good sanity check for user mode: <> *** link:userland/c/stderr.c[] *** File IO **** link:userland/c/file_write_read.c[] +***** link:userland/c/cat.c[]: a quick and dirty `cat` implementation for interactive <> tests **** link:userland/linux/open_o_tmpfile.c[]: https://stackoverflow.com/questions/4508998/what-is-an-anonymous-inode-in-linux/44388030#44388030 ** `time.h` *** link:userland/c/timespec_get.c[] `timespec_get` is a C11 for `clock_gettime` http://stackoverflow.com/questions/361363/how-to-measure-time-in-milliseconds-using-ansi-c/36095407#36095407 diff --git a/lkmc/file_io.h b/lkmc/file_io.h new file mode 100644 index 0000000..8af7200 --- /dev/null +++ b/lkmc/file_io.h @@ -0,0 +1,66 @@ +#ifndef LKMC_FILE_IO_H +#define LKMC_FILE_IO_H + +/* Returns the size of the given open `FILE*`. + * + * If an error occurs, returns `-1L`. + * + * Does not work for pipes. + */ +long lkmc_fget_file_size(FILE *fp) { + long oldpos; + long return_value; + oldpos = ftell(fp); + if (oldpos == -1L) { + return -1L; + } + if (fseek(fp, 0, SEEK_END) != 0) { + return -1L; + } + return_value = ftell(fp); + if (return_value == -1L) { + return -1L; + } + /* Restore the old position. */ + if (fseek(fp, oldpos , SEEK_SET) != 0) { + return -1L; + } + return return_value; +} + +/* Read the entire file to a char[] dynamically allocated inside this function. + * + * Returns a pointer to the start of that array. + * + * In case of any error, returns NULL. + * + * http://stackoverflow.com/questions/174531/easiest-way-to-get-files-contents-in-c + */ +char *lkmc_file_read(char *path) { + FILE *fp; + char *buffer; + long fsize; + + fp = fopen(path , "rb"); + if (fp == NULL) { + return NULL; + } + fsize = lkmc_fget_file_size(fp); + if (fsize < 0){ + fprintf(stderr, "could not determine lenght of:\n%s\n", path); + return NULL; + } + buffer = (char*)malloc(fsize); + if (buffer == NULL) { + return NULL; + } + if (fread(buffer, 1, fsize, fp) != (size_t)fsize) { + return NULL; + } + if (fclose(fp) == EOF){ + return NULL; + } + return buffer; +} + +#endif diff --git a/path_properties.py b/path_properties.py index fe0a924..6af370f 100644 --- a/path_properties.py +++ b/path_properties.py @@ -67,7 +67,6 @@ class PathProperties: 'no_build': False, # The path does not generate an executable in itself, e.g. # it only generates intermediate object files. Therefore it - # should not be run while testing. 'no_executable': False, 'qemu_unimplemented_instruction': False, # The script requires a non-trivial to determine argument to be passed to run properly. @@ -655,8 +654,8 @@ path_properties_tuples = ( 'test_run_args': {'cpus': 3}, }, 'assert_fail.c': {'signal_received': signal.Signals.SIGABRT}, - # This has complex failure modes, too hard to assert. - 'smash_stack.c': {'skip_run_unclassified': True}, + # Not sure which argument to pass. + 'cat.c': {'skip_run_unclassified': True}, 'exit1.c': {'exit_status': 1}, 'exit2.c': {'exit_status': 2}, 'false.c': {'exit_status': 1}, @@ -666,6 +665,8 @@ path_properties_tuples = ( 'm5ops.c': {'allowed_emulators': {'gem5'}}, 'return1.c': {'exit_status': 1}, 'return2.c': {'exit_status': 2}, + # This has complex failure modes, too hard to assert. + 'smash_stack.c': {'skip_run_unclassified': True}, # Wrapper not defined by newlib. 'timespec_get.c': {'baremetal': False}, } diff --git a/userland/c/cat.c b/userland/c/cat.c new file mode 100644 index 0000000..f348bdd --- /dev/null +++ b/userland/c/cat.c @@ -0,0 +1,13 @@ +#include +#include +#include + +#include + +int main(int argc, char **argv) { + assert(argc == 2); + char *s = lkmc_file_read(argv[1]); + puts(s); + free(s); + return EXIT_SUCCESS; +} diff --git a/userland/c/file_write_read.c b/userland/c/file_write_read.c index 08560f2..4b960c2 100644 --- a/userland/c/file_write_read.c +++ b/userland/c/file_write_read.c @@ -1,38 +1,12 @@ /* https://cirosantilli.com/linux-kernel-module-cheat#c */ #include +#include #include #include #include -/* Returns the size of the given open `FILE*`. - * - * If an error occurs, returns `-1L`. - * - * Does not work for pipes. - */ -long fget_file_size(FILE *fp) { - long oldpos; - long return_value; - oldpos = ftell(fp); - if (oldpos == -1L) { - return -1L; - } - if (fseek(fp, 0, SEEK_END) != 0) { - return -1L; - } - return_value = ftell(fp); - if (return_value == -1L) { - return -1L; - } - /* Restore the old position. */ - if (fseek(fp, oldpos , SEEK_SET) != 0) { - return -1L; - } - return return_value; -} - /* Same as `file_size`, but takes the path instead of a `FILE*`. */ long file_size(char *path) { FILE *fp; @@ -41,48 +15,13 @@ long file_size(char *path) { if (fp == NULL) { return -1L; } - return_value = fget_file_size(fp); + return_value = lkmc_fget_file_size(fp); if (fclose(fp) == EOF) { return -1L; } return return_value; } -/* Read the entire file to a char[] dynamically allocated inside this function. - * - * Returns a pointer to the start of that array. - * - * In case of any error, returns NULL. - * - * http://stackoverflow.com/questions/174531/easiest-way-to-get-files-contents-in-c - */ -char *file_read(char *path) { - FILE *fp; - char *buffer; - long fsize; - - fp = fopen(path , "rb"); - if (fp == NULL) { - return NULL; - } - fsize = fget_file_size(fp); - if (fsize < 0){ - fprintf(stderr, "could not determine lenght of:\n%s\n", path); - return NULL; - } - buffer = (char*)malloc(fsize); - if (buffer == NULL) { - return NULL; - } - if (fread(buffer, 1, fsize, fp) != (size_t)fsize) { - return NULL; - } - if (fclose(fp) == EOF){ - return NULL; - } - return buffer; -} - /* Write a null terminated string to file * * Return -1 on failure, 0 on success. @@ -117,9 +56,9 @@ int main(void) { /* Read entire file at once to a string. */ { - char *output = file_read(path); + char *output = lkmc_file_read(path); if (output == NULL) { - LKMC_IO_ERROR("file_read", path); + LKMC_IO_ERROR("lkmc_file_read", path); } assert(strcmp(data, output) == 0); free(output);