Fwrite function cuts off data in output .tar file - c

I have a piece of code that is designed to replicate the creation of a .tar file albeit a simpler version of it. It will take in 2 .txt files and produce a .tar file of the 2 files. Below is my code for doing so. However when opening the .tar file it is corrupted and sure enough, by viewing the data in a hex editor the data is cut off.
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
#define RECORDSIZE 512
#define NAMSIZ 100
#define TUNMLEN 32
#define TGNMLEN 32
struct header {
char name[NAMSIZ];//needed
char size[12];//needed
};
int main(int argc, char** argv) {
//argv[1] file1 argv[2] file2
char* file1 = argv[1];
char* file2 = argv[2];
FILE* f;
int lSize;
char temp_length[10];
char* file1_data, * file2_data;
int result;
//char* output_str = (char*)malloc(sizeof)
f = fopen(file1, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
lSize = ftell(f);
fseek(f, 0, SEEK_SET);
file1_data = (char*)malloc(sizeof(char) * lSize);
if (file1_data == NULL) {
printf("Memory error!");
return 1;
}
result = fread(file1_data, 1, lSize, f);
file1_data[result] = '\0';
fclose(f);
sprintf(temp_length, "%d", lSize);
struct header* h1 = malloc(sizeof(struct header));
strcpy(h1->name, file1);
strcpy(h1->size, temp_length);
printf("Name:%s Value:%s\n", h1->name, h1->size);
printf("File 1 data:%s\n", file1_data);
f = fopen(file2, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
lSize = ftell(f);
fseek(f, 0, SEEK_SET);
file2_data = (char*)malloc(sizeof(char) * lSize);
if (file2_data == NULL) {
printf("Memory error!");
return 1;
}
result = fread(file2_data, 1, lSize, f);
file2_data[result] = '\0';
fclose(f);
sprintf(temp_length, "%d", lSize);
struct header* h2 = malloc(sizeof(struct header));
strcpy(h2->name, file1);
strcpy(h2->size, temp_length);
printf("Name:%s Value:%s\n", h2->name, h2->size);
printf("File 2 data:%s\n", file2_data);
//allocate mem for output buffer
int total = sizeof(struct header) + sizeof(struct header) + sizeof(file1_data) + sizeof(file2_data);
printf("total length %d\n", total);
f = fopen("Result.tar", "wb");
//fwrite(input,length,no of ele,output buffer)
fwrite(h1, sizeof(struct header), 1, f);
fwrite(file1_data, sizeof(file1_data), 1, f);
fwrite(h2, sizeof(struct header), 1, f);
fwrite(file2_data, sizeof(file2_data), 1, f);
if (fwrite != 0)
printf("Contents to file written successfully !\n");
else
printf("Error writing file !\n");
fclose(f);
}
First file name: File1.txt
Data within:
This is File 1.
Second file name: File2.txt
Data within:
This is File 2.
File 2 has more data inside.
Decoded text output:
File1.txt�ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ31�ÍÍÍÍÍÍÍÍÍThis is File1.txt�ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ63�ÍÍÍÍÍÍÍÍÍThis is
As observed, File 2's name as well as the subsequent data has been cut off. I've been debugging but I'm unsure where am I going wrong as fwrite does not return a 0 hence, I'm assuming it's successful.
As the print statements are exactly what I expected, I don't think the reading of the data is the issue but rather the fwrite function. Hence I would like to seek advice on it.

The error resids in the way you calculate the total length. Indeed, you do not want to use sizeof(file1_data) as the length of the file. Instead you want to use the value returned when reading in result.
Create two variables file1_length and file2_length. Then populate them with the size of their respective file:
...
size_t f1_len, f2_len;
...
f = fopen(file1, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
f1_size = ftell(f);
fseek(f, 0, SEEK_SET);
file1_data = (char*)malloc(sizeof(char) * f1_len);
...
int total = sizeof(struct header) + sizeof(struct header) + f1_len + f2_len;
...
fwrite(h1, sizeof(struct header), 1, f);
fwrite(file1_data, f1_len, 1, f);
fwrite(h2, sizeof(struct header), 1, f);
fwrite(file2_data, f2_len, 1, f);
...
Finally, use these variable as the length of the files' content.
NOTE: The value obtained by sizeof(file1_data) indicate the size of the type. Here, since file1_data is of type char * you get 4.

Related

C6386 - Buffer overrun while writing to 'buffer' when using fread() function

"Warning C6386 Buffer overrun while writing to 'buffer': the writable size is 'Param(1)*Param(2)' bytes, but '4294967295' bytes might be written."
I'm writing a code to calculate a postfix expression using a stack implemented using linked lists, and I'm reading the postfix expression from a local file in binary ( fopen(filename, "rb" ) into a buffer.
I get the above mentioned warning at this line of code:
fread(buffer, sizeof(char), fileLength, file);
But, I've used calloc to allocate exactly the amount of memory I'd need based on the length of the file like this:
fseek(file, 0, SEEK_END);
fileLength = ftell(file);
buffer = (char*)calloc(fileLength + 1, sizeof(char));
if (!buffer) {
perror("Can't allocate memory!\n");
return NULL;
}
I don't understand where it got the "'4294967295' bytes might be written". Anyone care enough to explain what might be the cause, I'm a student and I'm not that much experienced with C.
Here's the entire function block:
int CalculatePostfix(double* destination, char* fileName)
{
FILE* file = NULL;
int fileLength = 0;
char* buffer = NULL;
char* currentBuffer = NULL;
int numBytes = 0;
char operation = 0;
double number = 0;
int status = EXIT_SUCCESS;;
StackElement head = { .number = 0, .next = NULL };
file = fopen(fileName, "rb");
if (!file) {
perror("Can't open file!\n");
return -1;
}
fseek(file, 0, SEEK_END);
fileLength = ftell(file);
buffer = (char*)calloc(fileLength + 1, sizeof(char));
if (!buffer) {
perror("Can't allocate memory!\n");
return NULL;
}
rewind(file);
fread(buffer, sizeof(char), fileLength, file);
printf("|%s|\n", buffer);
fclose(file);
currentBuffer = buffer;
while (strlen(currentBuffer) > 0) {
status = sscanf(currentBuffer, " %lf %n", &number, &numBytes);
if (status == 1) {
Push(&head, number);
currentBuffer += numBytes;
}
else {
sscanf(currentBuffer, " %c %n", &operation, &numBytes);
status = PerformOperation(&head, operation);
if (status != EXIT_SUCCESS) {
free(buffer);
while (head.next != NULL) {
DeleteAfter(&head);
}
return -1;
}
currentBuffer += numBytes;
}
}
free(buffer);
return EXIT_SUCCESS;
}
ftell returns a long integer, fread takes a size_t which, depending on implementation, often is an unsigned int. So if you happen to get -1L (which is the error return code from ftell) back from ftell you will end up vid a massive large unsigned int.
So to solve this, check return value of ftell and make sure it is not -1L, then when calling fread cast to size_t

Why does the measurement of the size of a file differ from the size of the string that contains it once buffered?

In order to write the content of a file in a buffer, I first need to know the size of the string to allocate. To do this, I use the following function:
long file_length(FILE *fp)
{
if (fp == NULL) return -1L;
fseek(fp, 0L, SEEK_END);
const long len = ftell(fp);
rewind(fp);
return len;
}
And I use it as follows to store the contents of my file:
char *file_content(const char *fname)
{
assert(access(fname, F_OK) != -1);
assert(access(fname, R_OK) != -1);
FILE *fp = fopen(fname, "r");
assert(fp != NULL);
const long flen = file_length(fp);
printf("Length of file: %ld\n", flen);
char *buff = malloc(flen + 1);
assert(buff != NULL);
fread(buff, sizeof(char), flen, fp);
buff[flen + 1] = '\0';
fclose(fp);
return buff;
}
And then I test:
int main()
{
char *content = file_content("test.txt");
printf("Length of buffer: %lld\n", strlen(content));
free(content);
return 0;
}
Here's test.txt:
Hello, world!
This is a simple test.
Stackoverflow.
My program then displays this:
Length of file: 57
Length of buffer: 53
As the file has 4 line feeds, I imagine that the result is related to their interpretation according to the different readings that are made (for the position of the file, with fseek, and for its buffering, with fread). But is it? Or maybe it changes depending on the platform or a reading mode?
If that's the case, so I would like to know how to get the same results, so that I can allocate the exact size of the string directly from my file_length function, without having to subtract the number of line feeds the file contains (if it's possible?) in order to be as optimal as possible.

I wrote an function which reads in an entire file into a string but it doesnt work

So, i need to read an entire file into a string in c, i dont know how big the file is gonna be. I wrote this function, but it doesnt work:
int slurp(char * filepath, char * outputfile) {
fp = fopen(filepath, "r");
int success = 0;
if (fp == NULL) {
success = 1;
}
if (success == 0) {
fseek(fp, 0, SEEK_END);
outputfile = (char *) calloc(ftell(fp) + 1, sizeof(char));
fread(outputfile, ftell(fp), sizeof(char), fp);
fseek(fp, 0, SEEK_SET);
outputfile[ftell(fp)] = '\0';
}
return success;
}
It doesnt get an error opening the file, but when i print out outputfile, i only get (null).
Why doesnt it work?
Thanks.
I tried your suggestions and it still doesnt work:
int slurp(char * filepath, char * outputfile) {
fp = fopen(filepath, "r");
int success = 0;
if (fp == NULL) {
success = 1;
}
if (success == 0) {
fseek(fp, 0, SEEK_END);
size_of_file = ftell(fp);
fseek(fp, 0, SEEK_SET);
outputfile = (char *) calloc(size_of_file + 1, sizeof(char));
fread(outputfile, size_of_file, sizeof(char), fp);
outputfile[size_of_file] = '\0';
}
return success;
}
Seek to the beginning before reading (reverse to this order):
fseek(fp, 0, SEEK_SET);
fread(outputfile, ftell(fp), sizeof(char), fp);

Problems with fread/malloc

FILE *infp, *outfp;
infp = fopen(argv[2], "r");
int len;
char *text;
fseek(infp, 0, SEEK_END);
len = ftell(infp);
printf("%d\n", len);
if ((text = (char *) malloc(500000000)) == NULL)
{
fprintf(stderr, "Error allocating memory\n");
exit(1);
}
fread(text, len, 1, infp);
text[len] = '\0';
fclose(infp);
printf("Text = %s, Address = %u\n", text, text);
returns
138
Text = , Address = 3794927632
I'm not sure why text isn't printing anything. Am I using fread wrong somehow?
You need to reset the file position with rewind() or fseek(3) like this
FILE *infp;
FILE *outfp;
int length;
char *text;
if ((infp = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "Error openning `%s'\n", argv[2]);
return -1;
}
fseek(infp, 0L, SEEK_END);
len = ftell(infp);
/* reset position */
fseek(infp, 0L, SEEK_SET); /* essentially rewind(infp); */
printf("%d\n", length);
if ((text = malloc(length + 1)) == NULL)
{
fprintf(stderr, "Error allocating memory\n");
return -1;
}
if (fread(text, 1, length, infp) == length)
{
text[length] = '\0';
printf("Text = %s, Address = %u\n", text, text);
free(text); /* never forget to `free' */
}
else
{
free(text);
text = NULL:
}
fclose(infp);
You also should
Check the return value of fopen(), you never check if the file was actually opened which I think is the main problem.
Allocate only the necessary space.
Ensure that fread() didn't fail.
Swap fread(3)'s size parameters, first is the element size and then the number of elements
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
and the return value should be equal to nmemb, read the manual page at the link above.
After
fseek(infp, 0, SEEK_END);
infp points to the end of the file. You need to rewind the file.
rewind(infp);
See http://www.cplusplus.com/reference/cstdio/rewind/ for additional info.

How to read the content of a file to a string in C?

What is the simplest way (least error-prone, least lines of code, however you want to interpret it) to open a file in C and read its contents into a string (char*, char[], whatever)?
I tend to just load the entire buffer as a raw memory chunk into memory and do the parsing on my own. That way I have best control over what the standard lib does on multiple platforms.
This is a stub I use for this. you may also want to check the error-codes for fseek, ftell and fread. (omitted for clarity).
char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");
if (f)
{
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length);
if (buffer)
{
fread (buffer, 1, length, f);
}
fclose (f);
}
if (buffer)
{
// start to process your data / extract strings here...
}
Another, unfortunately highly OS-dependent, solution is memory mapping the file. The benefits generally include performance of the read, and reduced memory use as the applications view and operating systems file cache can actually share the physical memory.
POSIX code would look like this:
int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
Windows on the other hand is little more tricky, and unfortunately I don't have a compiler in front of me to test, but the functionality is provided by CreateFileMapping() and MapViewOfFile().
If "read its contents into a string" means that the file does not contain characters with code 0, you can also use getdelim() function, that either accepts a block of memory and reallocates it if necessary, or just allocates the entire buffer for you, and reads the file into it until it encounters a specified delimiter or end of file. Just pass '\0' as the delimiter to read the entire file.
This function is available in the GNU C Library, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994
The sample code might look as simple as
char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
/* Success, now the entire file is in the buffer */
If you are reading special files like stdin or a pipe, you are not going to be able to use fstat to get the file size beforehand. Also, if you are reading a binary file fgets is going to lose the string size information because of embedded '\0' characters. Best way to read a file then is to use read and realloc:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main () {
char buf[4096];
ssize_t n;
char *str = NULL;
size_t len = 0;
while (n = read(STDIN_FILENO, buf, sizeof buf)) {
if (n < 0) {
if (errno == EAGAIN)
continue;
perror("read");
break;
}
str = realloc(str, len + n + 1);
memcpy(str + len, buf, n);
len += n;
str[len] = '\0';
}
printf("%.*s\n", len, str);
return 0;
}
Note: This is a modification of the accepted answer above.
Here's a way to do it, complete with error checking.
I've added a size checker to quit when file was bigger than 1 GiB. I did this because the program puts the whole file into a string which may use too much ram and crash a computer. However, if you don't care about that you could just remove it from the code.
#include <stdio.h>
#include <stdlib.h>
#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3
char * c_read_file(const char * f_name, int * err, size_t * f_size) {
char * buffer;
size_t length;
FILE * f = fopen(f_name, "rb");
size_t read_length;
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
// 1 GiB; best not to load a whole large file in one string
if (length > 1073741824) {
*err = FILE_TOO_LARGE;
return NULL;
}
buffer = (char *)malloc(length + 1);
if (length) {
read_length = fread(buffer, 1, length, f);
if (length != read_length) {
free(buffer);
*err = FILE_READ_ERROR;
return NULL;
}
}
fclose(f);
*err = FILE_OK;
buffer[length] = '\0';
*f_size = length;
}
else {
*err = FILE_NOT_EXIST;
return NULL;
}
return buffer;
}
And to check for errors:
int err;
size_t f_size;
char * f_data;
f_data = c_read_file("test.txt", &err, &f_size);
if (err) {
// process error
}
else {
// process data
free(f_data);
}
What is the simplest way (least error-prone, least lines of code, however you want to interpret it) to open a file in C and read its contents into a string ...?
Sadly, even after years, answers are error prone and many lack proper string formation and error checking.
#include <stdio.h>
#include <stdlib.h>
// Read the file into allocated memory.
// Return NULL on error.
char* readfile(FILE *f) {
// f invalid? fseek() fail?
if (f == NULL || fseek(f, 0, SEEK_END)) {
return NULL;
}
long length = ftell(f);
rewind(f);
// Did ftell() fail? Is the length too long?
if (length == -1 || (unsigned long) length >= SIZE_MAX) {
return NULL;
}
// Convert from long to size_t
size_t ulength = (size_t) length;
char *buffer = malloc(ulength + 1);
// Allocation failed? Read incomplete?
if (buffer == NULL || fread(buffer, 1, ulength, f) != ulength) {
free(buffer);
return NULL;
}
buffer[ulength] = '\0'; // Now buffer points to a string
return buffer;
}
Note that if the text file contains null characters, the allocated data will contain all the file data, yet the string will appear to be short. Better code would also return the length information so the caller can handle that.
char* readfile(FILE *f, size_t *ulength_ptr) {
...
if (ulength_ptr) *ulength_ptr == *ulength;
...
}
If the file is text, and you want to get the text line by line, the easiest way is to use fgets().
char buffer[100];
FILE *fp = fopen("filename", "r"); // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
If you're using glib, then you can use g_file_get_contents;
gchar *contents;
GError *err = NULL;
g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
{
// Report error to user, and free error
g_assert (contents == NULL);
fprintf (stderr, "Unable to read file: %s\n", err->message);
g_error_free (err);
}
else
{
// Use file contents
g_assert (contents != NULL);
}
}
Just modified from the accepted answer above.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
char *readFile(char *filename) {
FILE *f = fopen(filename, "rt");
assert(f);
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = (char *) malloc(length + 1);
buffer[length] = '\0';
fread(buffer, 1, length, f);
fclose(f);
return buffer;
}
int main() {
char *content = readFile("../hello.txt");
printf("%s", content);
}
// Assumes the file exists and will seg. fault otherwise.
const GLchar *load_shader_source(char *filename) {
FILE *file = fopen(filename, "r"); // open
fseek(file, 0L, SEEK_END); // find the end
size_t size = ftell(file); // get the size in bytes
GLchar *shaderSource = calloc(1, size); // allocate enough bytes
rewind(file); // go back to file beginning
fread(shaderSource, size, sizeof(char), file); // read each char into ourblock
fclose(file); // close the stream
return shaderSource;
}
This is a pretty crude solution because nothing is checked against null.
I will add my own version, based on the answers here, just for reference. My code takes into consideration sizeof(char) and adds a few comments to it.
// Open the file in read mode.
FILE *file = fopen(file_name, "r");
// Check if there was an error.
if (file == NULL) {
fprintf(stderr, "Error: Can't open file '%s'.", file_name);
exit(EXIT_FAILURE);
}
// Get the file length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Create the string for the file contents.
char *buffer = malloc(sizeof(char) * (length + 1));
buffer[length] = '\0';
// Set the contents of the string.
fread(buffer, sizeof(char), length, file);
// Close the file.
fclose(file);
// Do something with the data.
// ...
// Free the allocated string space.
free(buffer);
easy and neat(assuming contents in the file are less than 10000):
void read_whole_file(char fileName[1000], char buffer[10000])
{
FILE * file = fopen(fileName, "r");
if(file == NULL)
{
puts("File not found");
exit(1);
}
char c;
int idx=0;
while (fscanf(file , "%c" ,&c) == 1)
{
buffer[idx] = c;
idx++;
}
buffer[idx] = 0;
}

Resources