Reading a mapped file and storing it in a buffer - c

Can someone kindly explain how I can implemented an mread function, by using the read() system call. This method needs to read contents which are found in the mmapped file and read them into a buffer. I have access to both the mmapped file and buffer by means of pointers. (i.e. void *addr and void *buff).
Your help is v.much appreciated.
Try so far:
int fd;
if ((fd = open("file.hole",O_RDWR, "rb")) < 0) {
perror("create .hole file error");
exit(EXIT_FAILURE);
}
if (write(fd, addr, count)!= count) {
perror("Cannot write from address");
exit(EXIT_FAILURE);
}
buff = (char*)malloc(count * sizeof(char *));
if (read(fd, buff, count)) {
perror("Cannot read from file descriptor to the buffer");
exit(EXIT_FAILURE);
}

Sorry, this might not a complete solution, but I don't have sufficient reputation to add only comments.
If you need to read data from mmaped file (assumed that you have called mmap() on a file), you don't need read() system call; you just need to copy the content from addr to buff.
If you need read() system call to copy data from a file to a buffer, you don't need mmap(); you should just do open() on the file to get an fd and then do read() data from the fd to the buffer.

Related

Bus Error when writing to mmaped data

When I first made this project last semester, the code worked fine. Now I get a bus error when the mmapped memory to share between processes is being written to and I'm not sure why it is not working anymore.
Account_Info *mapData()
{
int fd;
//open/create file with read and write permission and check return value
if ((fd = open("accounts", O_RDWR|O_CREAT, 0644)) == -1)
{
perror("Unable to open account list file.");
exit(0);
}
//map data to be shared with different processes
Account_Info *accounts = mmap((void*)0, (size_t) 100*(sizeof(Account_Info)), PROT_WRITE,
MAP_SHARED, fd, 0);
int count= 0;
//loop to initialize values of Account_Info struct
while (count != 20)
{
//bus error occurs here
accounts[count].CurrBalance= 0;
accounts[count].flag = 0;
int i = 0;
while (i != 100)
{
//place NULL terminator into each element of AccName
accounts[count].AccName[i]= '\0';
i++;
}
count++;
}
close(fd);
return accounts;
}
A documented cause for SIGBUS with mmap is
Attempted access to a portion of the buffer that does not correspond to the file (for example, beyond the end of the file, including the case where another process has truncated the file).
My guess is that the accounts file didn't exist, so open with O_CREAT created it. But it has zero size, so any attempt to read or write through the mapping will fault. You need to fill the file with enough zeroes (or something else) to cover the mapping, for example using ftruncate.
You will get SIGBUS if you attempt to write past the mapped region of the file.
Chances are pretty good that your backing store file accounts is truncated/too short. (e.g.) if the file has space for 10 struct entries and you write to the 11th, you'll get SIGBUS
Do an fstat to get st_size and compare this against the length parameter you're giving to mmap
You may want to consider using ftruncate to extend the file before doing mmap

Client does not read any data after reading another file

I am writing a simple client/server application and I ran into a very weird issue.
I am trying to send a .zip file to the client and some more data after that.
Sending the .zip works fine, the server writes to the socket, the client reads from the socket, just as intended.
The problem is right after that.
The server runs fine and keeps writing to the socket, but the client just won't read anything. It gets stuck on the very next read() call, no matter what I try to send.
I've checked to see if the socket descriptors are alright, and they are. I also thought that maybe there is not enough data in the socket for the client to read, but there definitely is.
I also tried doing the same write/read before and after sending that .zip file: it works fine before, but client doesn't see it after sending that .zip.
I am out of ideas.
Here's the function I use to send the .zip:
typedef struct thData{
int idThread; //thread ID
int cl; //client descriptor
}thData;
void send_info(struct thData tdL)
{
char file_path[256]="v1.zip";
char sd_buffer[256];
bzero(sd_buffer, 256);
FILE *fd = fopen(file_path, "rb");
if(fd == NULL)
{
printf("ERROR: %s not found.\n", file_path);
exit(1);
}
int read_size;
int write_size;
while((read_size = fread(sd_buffer, sizeof(char), 256, fd)) > 0)
{
if((write_size=write(tdL.cl, sd_buffer, read_size)) < 0)
{
perror("ERROR: writing to client: \n");
break;
}
bzero(sd_buffer, 256);
}
}
And what I use to receive the .zip:
void receive_info(int sd) //sd being the socket descriptor
{
char* file_path = "subject.zip";
char received_buffer[256];
int total_received=0;
int total_wrote=0;
FILE *fd = fopen(file_path, "wb");
if(fd == NULL)
printf("Cannot open %s\n", file_path);
else
{
bzero(received_buffer, 256);
int read_size = 0;
while((read_size = read(sd, received_buffer, 256)) > 0)
{
total_received=total_received+read_size;
int write_size = fwrite(received_buffer, sizeof(char), read_size, fd);
total_wrote=total_wrote+read_size;
if(write_size < read_size)
{
perror("ERROR: \n");
}
bzero(received_buffer, 256);
if (read_size == 0 || read_size != 256)
{
break;
}
}
if(read_size < 0)
{
perror("ERROR: reading: ");
exit(1);
}
fclose(fd);
}
}
Any help would be greatly appreciated.
I think that the problem is that you are reading too much.
In TCP there are no boundaries in the packets sent from one peer to the other. It is just a stream of bytes, and the pieces received from recv/read() bear no relation (in principle) to the pieces sent from send/write().
Now, imagine that your ZIP file is 300 bytes long, and your extra data is 10 bytes long. Your sender code will do:
write 256 bytes (first piece from ZIP).
write 44 bytes (last piece from ZIP).
write 10 bytes (the extra data).
And your receiver code will do:
read 256, get 256 bytes (first piece from ZIP).
read 256, get 54 bytes (last piece from ZIP plus the extra data).
read XXX bytes, wait forever!
If you look carefully to the ZIP files you'll probably see those extra bytes at the end of subject.zip.
The solution, if you don't want to close and open another socket, is to make the protocol a bit more complicated. For example, you could send a structure before the file (a header) that includes the size of the file. That way the receiver will know when to stop reading.
PS: Note that your code has a few risky edges. For example, write() might not write all the given bytes, but you are not checking that; you are not closing the file...
PS2: I find curious that you feel the need to write sizeof(char) instead of just 1 but you write 256 instead of sizeof(sd_buffer).

Using mmap to receive file from server

Say I'm sending a file from a server to a client using read & send system call.
Now, I want to receive the data at the client side using mmap system call. how do I do that?
given the following send_file function (Server side) :
(sd is the socket descriptor associated with the client)
int send_file (int sd, const char* file_name) {
int fd;
char buf[1024];
if ( (fd =get_fd(file_name)) > 0) {
while (read(fd, buf, sizeof buf) > 0) {
if (send(sd, buf, sizeof buf, 0) < 0) {
perror("send");
return -1;
}
}
close(fd);
return 0;
}
return -1;
}
Again, I want to create the file at the client's side and then use MMAP to store the file from the server.
How do I do that? would love to get some suggestions.
thanks in advance
You can't receive a file from a socket using mmap. mmap is used to map files (or anonymous memory) into your process virtual adress space. Quoting the man page
mmap() creates a new mapping in the virtual address space of the
calling process.
So you have to use "sockets calls" to receive the file at the client side.
Not sure to understand why you want to do that, here is a way using mmap to write into the file at client side. First you have to use fopen(). You can then use lseek to "enlarge" the file:
The lseek() function allows the file offset to be set beyond the
end of the file (but this does not change the size of the file). If
data is later written at this point, subseā€
quent reads of the data in the gap (a "hole") return null bytes ('\0') until data is actually written into the gap.
And finally you can mmap it and copy the content received through network.

linux virtual file as device driver

I write a linux char device driver to simulate a file. The data is stored in an array and I want to implement a "read-file"-handler...
static ssize_t data_read(struct file *f, char __user *buf, size_t count, loff_t *f_pos){
char *msg_pointer;
int bytes_read = 0;
if(vault.storage==NULL)
return -EFAULT;
msg_pointer = vault.storage + *f_pos;
while (count && (*f_pos < vault.size) ) {
put_user(*(msg_pointer++), buf++);
count--;
bytes_read++;
++*f_pos;
}
return bytes_read;
}
vault.storage is a pointer to a kmalloc-creation. If I test the code by copying with dd it works as expected, but when I want to open the file with C
if((fp_data = open("/dev/vault0", O_RDWR)) < 0){
perror("could not open file.\n");
}
err = write(fp_data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 36);
if (err < 0){
perror("failed to write to sv \n");
}
read(fp_data, buffer, 36);
read(fp_data, buffer, 36);
the first read-command returns 4.. the second 0 - how is this possible?
write performed on a file is not guaranteed to write all the bytes requested atomically ... that is only reserved for a pipe or FIFO when the requested write-amount is less than PIPE_BUF in size. For instance, write can be interrupted by a signal after writing some bytes, and there will be other instances where write will not output the full number of requested bytes before returning. Therefore you should be testing the number of bytes written before reading back any information into a buffer to make sure you are attempting to read-back the same number of bytes written.
Put a printk in the data_read call and print the count and print what is returned to the user(check the value of bytes_read). The bytes_read is returned to the read() call in the use space. Make sure you are returning correct value. And you can also print the fpos and check what is happening.
Here I assume that your drivers read and write functions are called properly, I mean major and minor numbers of your device file belongs to your driver

system call to map memory to a file descriptor (inverse mmap)?

I want to be able to map memory to a file descriptor so I can use some existing functions that need a file descriptor. Here's essentially what I'm looking for:
void do_operation1(int fd);
char data[DATA_MAX] = { /* embedded binary data */ };
int fd = addr_to_fd(data, DATA_MAX);
do_operation1(fd);
/* ... operate on fd ... */
What system call, or calls, can I use to accomplish this?
Some implementations have fmemopen(). (Then of course you have to call fileno()).
If yours doesn't, you can build it yourself with fork() and pipe().
You should Check out shm_open().
Sure, just open(argv[0], ...), scan through the file to find where your binary data starts, lseek() there and done. That file won't have the length of your binary data of course.
You cannot map "some existing memory buffer" to a file descriptor. As said in a comment above, the fmemopen() function associates a memory buffer with a "FILE *" stream pointer which can be manipulated with the libc-provided streams functions. No file descriptor is allocated: the "FILE *" stream is a high-level abstraction and is NOT compatible with a file descriptor which is a low-level handle.
Instead, you may want to allocate a new shared memory buffer and map it to a file descriptor. This is widely used and known as a "memory-mapped file" in the Linux jargon.
You can use a file descriptor obtained with open() that refers to a file or a file descriptor obtained with shm_open() that refers to a shared memory object. Any file descriptor handle will do the job. You can then invoke mmap() to map the file descriptor to a shared memory buffer.
Note: mmap() will fail if the file descriptor refers to a non-regular file such as a pipe, a socket or a character device file (e.g., /dev/ttys001). Due to this, you cannot usually create a memory-mapped file for the STDIN, STDOUT or STDERR file descriptors.
You can manipulate the memory buffer in an array-like fashion and modifications to the memory-mapped file are committed to disk. The opposite is also true, with any modifications made to the file (e.g., with a write() syscall) committed to memory as well.
The following snippet opens a file of your choice and maps it into memory. It will print the first 256 characters and replace them with "*" in the original file. Compile it with cc mmap_test.c -o mmap_test on a POSIX-compliant system.
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
int main(int ac, char *av[])
{
int pagesize, fd;
unsigned char *data;
if ( ac < 2 ) {
printf("Usage: %s <filepath>\n", av[0]);
return 1;
}
pagesize = getpagesize();
if ( (fd = open(av[1], O_RDWR)) == -1 ) {
perror("Error: cannot open file for reading");
return 1;
}
if ( (data = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ) {
perror("Error: cannot create memory-mapped file");
return 1;
}
write(1, data, 256);
memset(data, '*', 256);
return 0;
}

Resources