What to use instead of sizeof(void)? - c

I'm trying to copy a file. I'm using a borrowed code snippet, and there's a line of it which errors which confuses me.
int fileread = open("original.txt", O_RDONLY);
void *buffer;
buffer = malloc(sizeof(void) * size); /*This line gives "Incomplete type not allowed."*/
int nread = read(fileread,buffer,size);
int filewrite = open("original.txt.backup",O_CREAT | O_RDWR, 0644);
write(filewrite,buffer,size);
close(filewrite);
close(fileread);
What should I be using instead? I was thinking char*, but I want to make sure I'm understanding the process going on here.

If you want to allocate a buffer of size bytes, and have buffer point to the beginning of it:
void *buffer = malloc(size);
if (buffer == NULL) {
/* allocation failed */
}
/* ... */
But if you want to do something with the data in the buffer, it will need to have a valid type. An array of unsigned char is a common way to manage buffers of arbitrary contents:
unsigned char *buffer = malloc(size);
/* as above */

sizeof() returns the size of your type. Honestly I think you should just change it to
char *buffer;
buffer = malloc(sizeof(char) * size);
Sizeof(void) makes zero sense

Related

How to convert size_t to char* in kernel C?

I use a size_t variable in my kernel module. when I want to write it to a file it must be cast to char* according to vfs_write signature:
extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
I use this function that uses vfs_write (I found it on internet):
int file_write(struct file *file, unsigned long long offset, unsigned
char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
the nbytes variable is size_t I try (char *) cast for convert nbytes to char* but the kernel immediately crashes. here is my code.
index_filename = "/home/rocket/Desktop/index_pool";
index_file = file_open(index_filename,O_WRONLY | O_CREAT, 0644);
if(index_file == NULL)
printk(KERN_ALERT "index_file open error !!.\n");
else{
// file_write(index_file, 0, nbytes, nbytes); => this crashs also
file_write(index_file, 0, (char*) nbytes, 100);
file_close(index_file);
}
Is there a way to safely convert size_t type to char * one in Kernel ?
Of course it would crash - you're trying to write 100 bytes of whatever memory location nbytes is pointing at. Which because it isn't a pointer is extremely unlikely to be a valid area of memory. And even if it was, it might not be 100 bytes in size.
What you want to be passing instead to vfs_write is a pointer to nbytes. And the size of that would be sizeof(nbytes). So you'd call your wrapper function like this
file_write(index_file, 0, (char*) &nbytes, sizeof(nbytes));
That will write out the how ever many bytes that a size_t is at the memory location of nbytes
If you want to write out the value of nbytes, which is different to what you appear to be asking in the question, you need to store it in a string and pass that to your function like this:
char temp_string[20];
sprintf(temp_string,"%zu",nbytes);
file_write(index_file, 0, temp_string, strlen(temp_string));
Is there a way to safely convert size_t type to char * one in Kernel ?
yes there is.
you should use sprintf function in linux/kernel.h library
so you should do something like this:
sprintf(destination_char_star, "%zu", your_s_size_var);
be carefull you should allocate memory to char star if needed.

Pointers to Pointers: What datatype with variable length strings?

I need to store a list of names in C. I decided a good way to do this would be to use double pointers (or pointers-to-pointers).
So I will have an array of names. Each array value is a pointer to another array: the name, stored as a character array.
The names might look like
unsigned char *name = "Joseph";
etc.
And then I would have a list of names (double pointer), to which I could (dynamically) assign each name (single pointer).
(type) **listOfNames;
My question is: what type should the double pointer be? Since it is a pointer to a pointer, I was thinking that it must be large enough to hold an arbitrary memory address: unsigned long, maybe? Or is there another type meant specifically for double pointers?
Alternatively — is there a better way of storing a variable-length array or variable-length strings?
If you have a collection of unsigned char * values:
unsigned char *name_0 = "Joseph";
unsigned char *name_1 = "Helen";
unsigned char *name_2 = "Maximillian";
Then you can create:
unsigned char *data[] = { name_0, name_1, name_2 };
unsigned char **listOfNames = data;
Note that you need to allocate space for the list of pointers — in my code, that's the data array. You could use malloc() instead, but you'd need to remember to code the matching free() at some point.
Adding appropriate const qualifications is left as an exercise in frustration to the interested reader.
If you are reading a variable length list of names from a file a run time, with (presumably) one name per line, then you'd do best with POSIX getline() and strdup().
There must be a number of other questions that deal with this scenario, so I'll be terse with this code:
char **read_file(FILE *fp)
{
char *buffer = 0;
size_t bufsiz = 0;
char **lines = 0;
size_t n_lines = 0;
size_t n_alloc = 0;
while (getline(&buffer, &bufsiz, fp) > 0)
{
if (n_lines + 1 >= n_alloc)
{
size_t new_num = (n_alloc + 1) * 2;
size_t new_size = new_num * sizeof(*lines);
char **new_lines = realloc(lines, new_size);
if (new_lines == 0)
{
free(buffer);
free(lines);
return(0);
}
lines = new_lines;
n_alloc = new_num;
}
lines[n_lines++] = strdup(buffer); // Includes newline!
}
lines[n_lines] = 0; // Null terminate list of strings
free(buffer); // Release input line's memory
return lines;
}
Note that the code uses plain char and not unsigned char. You face some issues if you use unsigned char because neither getline() nor strdup() expects to work with unsigned char. While you can cast your way around the issue, it is messy to do so.

Modify buffer passed as a pointer

I'm trying to read into a buffer passed as a pointer to this function. memcpy() works fine and the data is stored correctly in buffer, but when I access buffer outside of the function it is null. There's some pointer issue I'm not getting here.
Here's the code, I took out most of it, I know it copies the data correctly, but it doesn't pass it to the buffer pointer. Ideas?
int read(file file, char *buffer , int maxlen) {
int bytes_read;
// copy data to file buffer
bytes_read = min(maxlen, file->file_size - file->cursor);
buffer = (char*) malloc(bytes_read);
memcpy(buffer , file->buffer + file->cursor, bytes_read);
return bytes_read;
}
The problem is pretty simple: you are modifying the variable "buffer". Since it is passed by value and not by reference, the calling function doesn't see the change. In order to make the change to buffer visible, you need to pass in a pointer to buffer.
Your function would then look like this:
int read(file file, char **buffer , int maxlen) {
int bytes_read;
// copy data to file buffer
bytes_read = min(maxlen, file->file_size - file->cursor);
*buffer = (char*) malloc(bytes_read);
memcpy(*buffer , file->buffer + file->cursor, bytes_read);
return bytes_read;
}
to call the function:
rv = read(file, &buffer, maxlen);
You cannot modify buffer directly because C uses pass by value with parameters. Therefore it is a copy of the pointer you are modifying. To change the pointer you need to change your function prototype to take a char** and allocate to the first level of indirection on that.
As a crude example of this:
void read(char** buffer , int byte_size) {
*buffer = (char*) malloc(byte_size);
}
and use where required with something like
char* buffer;
read(&buffer,10); /* now buffer points to dynamically allocated array of 10 chars */

Segmentation fault in reading a .dat file

Right now, I'm working on an example code that I wish to integrate into my program later. What essentially I'm trying to do is read a .dat file byte by byte and interpret the data (ie. interpret boot sector to output the sector size, reserved sectors etc.)
To do this, I am reading the data byte by byte and, using the descriptions in fat12 of https://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html#ss1.3 , I translate the data into the information that I want. Right now, I can pull individual bytes from the file (Is it right to assume that the data pulled is in hex?). However, I need two bytes to have something meaningful. So, I need to combine two bytes into one, convert the hex data into decimal and output the information. Unfortunately, right now, I'm getting a seg fault and for the life of me, I can't figure out what's wrong. Thanks in advance!
int main (int argc, char **argv){
FILE *fp ,*fptest;
long lSize;
char *buffer;
//Open file
fptest= open("fat_volume.dat", "rb");
//Read file into buffer
fread(buffer,1,512,fptest);
//Parse the boot sector
char tmpA, tmpB;
tmpA = buffer[10]; //First byte
tmpB = buffer[11]; //Second byte
//Combine the two bytes into one
char combinedBytes[3];
strcpy (combinedBytes, tmpA);
strcat (combinedBytes, tmpB);
//Hex to decimal converter
long int li1;
li1 = strtol (combinedBytes,NULL,16);
printf ("The sector size is: %ld.\n", li1);
return 0;
}
You must allocate buffer; e.g.
char buffer[512];
or
char *buffer = malloc(512);
EDIT:
The string operations
strcpy (combinedBytes, tmpA);
strcat (combinedBytes, tmpB);
do not make sense either and access/copy too much data (the compiler will warn you about this!).
I suggest do read values as
unsigned char tmpA = buffer[10];
unsigned char tmpB = buffer[11];
unsigned int tmp = (tmpA << 8) | (tmpB << 0); /* or revert in in case of
little-endian */
To make things more efficient, I would write it as
struct fat_header {
uint8_t pad0[10];
uint16_t my_val;
uint8_t pad1[500];
} __attribute__((__packed__)); /* this is not portable and for gcc! */
...
struct fat_header hdr;
fread(&hdr, 1, sizeof hdr, f);
uint16_t val = be16toh(hdr.my_val); /* or le16toh() in case of le */
You are reading into a buffer you never allocated memory for.
What you're trying now is to read from some junk value in memory, who knows, which almost always leads to a segmentation fault.
Use:
char *buffer = malloc(512 * sizeof(char)); // this allocates 512 times the size of a single char of memory
If you don't specify the number inside malloc to be of a specific size (e.g. malloc(512) the number is in bytes, though I think it's better to always include it.
This specific error is called dereferencing a null pointer
EDIT:
I've managed to run this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
FILE *fp ,*fptest;
long lSize;
char *buffer;
//Open file
fptest = fopen("input.txt", "rb");
if (fptest == NULL)
{
printf("Error occurred when opening file");
return 1;
}
buffer = malloc(sizeof(char) * 512);
//Read file into buffer
fread(buffer,1,512,fptest);
//Parse the boot sector
char tmpA, tmpB;
tmpA = buffer[10]; //First byte
tmpB = buffer[11]; //Second byte
//Combine the two bytes into one
char combinedBytes[3];
strcpy (combinedBytes, &tmpA);
strcat (combinedBytes, &tmpB);
//Hex to decimal converter
long int li1;
li1 = strtol (combinedBytes,NULL,16);
printf ("The sector size is: %ld.\n", li1);
return 0;
}
You also used a function open() which must be fopen(), and you need to pass the address of tmpA and tmpB to strcpy and strcat.
This is why I don't understand why your compiler doesn't give any errors or warnings..

C - how to correctly use malloc'ed array with C getline function?

I want to use previously malloc'ed array with a C getline function:
ssize_t getline(char **restrict, size_t *restrict, FILE *restrict)
The following code gives me EXC_BAD_ACCESS (code=1, address=0x400) :
FILE *in; if ((in=fopen(inpath, "r+w"))==NULL) exit(1);
char * buf = (char *) malloc (BUFSIZ); // BUFSIZ is constant, equal to 1024
if (getline(&buf, (size_t *)BUFSIZ, in)<0) return 1; // <--- EXC_BAD_ACCESS
How should I modify the code to make it working?
What you are doing now essentially tells getline there's a pointer to the address 1024 and you really want it to dereference it. Pass a real address as the second argument, don't cast an int and hope for the best.
size_t size = BUFSIZ;
getline(&buf, &size, in);

Resources