mmap is wiping my file instead of copying it - c

So I'm using mmap to then write to another file. But the weird thing is, when my code hits mmap, what it does is clears the file. So I have a file that's populated with random characters (AB, HAA, JAK, etc...). What it's supposed to do is use mmap as read basically and then write that file to the new file. So that first if (argc == 3) is the normal read and write, the second if (argc ==4) is supposed to use mmap. Does anyone have any idea why on Earth this is happening?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char const *argv[])
{
int nbyte = 512;
char buffer[nbyte];
unsigned char *f;
int bytesRead = 0;
int size;
int totalBuffer;
struct stat s;
const char * file_name = argv[1];
int fd = open (argv[1], O_RDONLY);
int i = 0;
char c;
int fileInput = open(argv[1], O_RDONLY);
int fileOutPut = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
fstat(fileInput, &s);
size = s.st_size;
printf("%d\n", size);
if (argc == 3)
{
printf("size: %d\n", size);
printf("nbyte: %d\n", nbyte);
while (size - bytesRead >= nbyte)
{
read(fileInput, buffer, nbyte);
bytesRead += nbyte;
write(fileOutPut, buffer, nbyte);
}
read(fileInput, buffer, size - bytesRead);
write(fileOutPut, buffer, size - bytesRead);
}
else if (argc == 4)
{
int i = 0;
printf("4 arg\n");
f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fileInput, 0);
/* This is where it is being wipped */
}
close(fileInput);
close(fileOutPut);
int who = RUSAGE_SELF;
struct rusage usage;
int ret;
/* Get the status of the file and print some. Easy to do what "ls" does with fstat system call... */
int status = fstat (fd, & s);
printf("File Size: %d bytes\n",s.st_size);
printf("Number of Links: %d\n",s.st_nlink);
return 0;
}
EDIT: I wanted to mention that the first read and write works perfectly, it is only when you try to do it through the mmap.

If you mean it's clearing your destination file, then yes, that's exactly what your code will do.
It opens the destination with truncation and then, in your argc==4 section, you map the input file but do absolutely nothing to transfer the data to the output file.
You'll need a while loop of some description, similar to the one in the argc==3 case, but which writes the bytes in mapped memory to the fileOutput descriptor.

Related

Reading and writing pdf or binary data in C

I'm implementing ftp and I want to upload and download files, when I download or upload pdf files they are corrupted. How can handle reading any file, using read() and write() or mmap? below is my simplified code of what I have tried.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int is_regular_file(const char *path)
{
struct stat path_stat;
stat(path, &path_stat);
return (S_ISREG(path_stat.st_mode));
}
int ft_get_file_size(const char *filename)
{
struct stat file;
int fd;
if (!is_regular_file(filename))
return (-1);
fd = open(filename, O_RDONLY);
memset(&file, 0, sizeof(struct stat));
fstat(fd, &file);
close(fd);
return (file.st_size);
}
char *read_file(const char *filename)
{
char *content;
int file_size;
int fd;
ssize_t retval;
if ((file_size = ft_get_file_size(filename)) <= 0)
return (NULL);
content = (char *)malloc(sizeof(char) * file_size + 1);
fd = open(filename, O_RDONLY);
retval = read(fd, content, file_size);
content[retval + 1] = '\0';
close(fd);
return (content);
}
void write_file(char *file, char *content)
{
int fd;
fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd)
write(fd, content, strlen(content));
close(fd);
}
int main() {
char *test = read_file("ftp.en.pdf");
write_file("copy.pdf", test);
return EXIT_SUCCESS;
}
The process of downloading and uploading the file, is reading all the data from the file and then send that data to the socket. I have tried using mmap and I still get corrupted file.
Document is damaged error message
As binary data can have \0 characters, you cannot treat your content as a string, so strlen(content) is wrong. You must return the size of the content from your read_file function.
For example, define your function as char *read_file(const char *filename, int *size) and return the size in *size. Likewise define your write function as void write_file(char *file, char *content, int size)
(and forget the +1 in malloc)

got bus error when writing to mmap'ed file

Trying to use mmap to write to a file. Unfortunately the first write in the loop map[i] = i; will cause a bus error. Not sure why.
The PC runs Ubuntu 14.04 and the file /tmp/mmapped.bin has 12 bytes and the program is invoked with ./a.out 3.
Thanks
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FILEPATH "/tmp/mmapped.bin"
//#define NUMINTS (1000)
#define FILESIZE 0x400000000
int main(int argc, char *argv[])
{
int i;
int fd;
int *map; /* mmapped array of int's */
int size = atoi(argv[1]);
fd = open(FILEPATH, O_RDWR| O_CREAT | O_TRUNC);
if (fd == -1) {
perror("Error opening file for reading");
exit(EXIT_FAILURE);
}
map = mmap(0, 4 * size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
for (i = 1; i <= size; ++i) {
map[i] = i;
}
if (munmap(map, FILESIZE) == -1) {
perror("Error un-mmapping the file");
}
close(fd);
return 0;
}
In c you need to start at index 0. Because it will simply increment the pointer by the amount i and then dereference it. Your code dereferences the pointer beyond the allowed bound.
It should be,
for (i = 0; i < size; ++i) {
map[i] = i;
}
because it's equivalent to
for (i = 0; i < size; ++i) {
*(map + i) = i;
}
Also, use
map = mmap(0, size * sizeof *map, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
to ensure that enough space is allocated and that *(map + i) will be inside bounds. Don't use magic numbers.
According to the mmap man page a bus error (SIGBUS) happens when you read/write outside the bounds of the file.
The length of the mapping is separate from the length of the file. If your file is newly created its size will be 0, even if you specify a length with mmap. Resize the file with ftruncate after opening it.

Using mmap to reverse a text file in place -- getting bus error

I thought i had it figured out but i'm getting a bus error. All it has to do is take some text file, use mmap and then reverse the contents without a temp file. What i did was map it, and then erase the file and write it from memory by starting at the end of the mmap pointer. This worked when I did it with cout, but for some reason doing it to a file i get the error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/io.h>
#include <sys/mman.h>
main(int argc, char *argv[])
{
unsigned char *f, *g;
int size;
struct stat s;
const char * file_name = argv[1];
int fd = open(argv[1], O_RDONLY);
int status = fstat(fd, &s);
size = s.st_size;
int i;
f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0);
//g = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0);
for(i = 0; i < size; i++) {
char c;
c = f[i];
putchar(c);
}
//ABOVE THIS WORKS
// int z = 0;
//while(f[z] != NULL) {
//z++;
// printf("%d", z);
// }
int x;
int y = 0;
close(fd);
FILE *f1;
f1 = fopen(argv[1], "w+");
for(x = size - 1; x >= 0; x--)
{
char c;
c = f[x];
fputc(c, f1);
}
}
Because you fopened the file with w, you truncated the file to 0 length. The mmap man page says that:
The effect of changing the size of the underlying file of a mapping on the pages that correspond to added or removed regions of the file is unspecified.
Anyways, it seems to me that you should call mmap with PROT_WRITE also, so that you can just reverse the array f in memory. Then you don't have to open the file again. Make sure to use MMAP_SHARED, and to also call munmap() after you are finished modifying the shared memory. You need MMAP_SHARED because with MMAP_PRIVATE:
Updates to the mapping are not visible to other processes mapping the same file, and are not carried through to the underlying file.
You should call munmap() because:
The file may not actually be updated until msync(2) or munmap() is called.
If you exit the program without calling munmap(), the memory will automatically be unmapped for you. But it's a good habit to close/free/unmap things yourself instead of just exiting.
(Edit: Thanks Adam Rosenfield and EOF for the corrections to my original answer.)

write() not writing to device

code:
#include <fcntl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
void write_zero(char * file, unsigned long bytes)
{
printf("Zeroing %s\n", file);
unsigned int wrote = 0, total = 0;
int fd, i, buf;
char obj = 0x00;
fd = open(file, O_RDWR, DEFFILEMODE);
lseek(fd, 0, SEEK_SET);
write(fd, &obj, bytes);
}
int main(int argc, char * * argv)
{
int fd;
unsigned long blocks = 0;
char check = 0x0;
fd = open(argv[1], O_RDONLY);
ioctl(fd, BLKGETSIZE, &blocks);
close(fd);
printf("Blocks: %lu\tBytes: %lu\tGB: %.2f\n",
blocks, blocks * 512, (double)blocks * 512.0 / (1024 * 1024 * 1024));
do
{
printf("Write 0x0 to %s? [y/N] ", argv[1]);
fflush(stdout);
}
while (scanf("%c", &check) < 1);
if (check == 'y')
{
write_zero(argv[1], blocks * 512);
}
}
I get nothing actually written to the device.. I copied my open line from the 'dd' source code, thinking maybe it was not opened right. dd can zero the device, but this program does not. Any ideas?
It seems like this has been beaten to death but
char obj = 0x00;
fd = open(file, O_RDWR, DEFFILEMODE);
lseek(fd, 0, SEEK_SET);
write(fd, &obj, bytes);
Is not going to write zeros. It's going to write garbage from the stack.

How does read(2) interact with carriage returns?

I am writing a simple program to flip all the bits in a file, but right now it only does the first 1000 bytes until I get that much working. Why does my call to read() ignore \r characters? When I run this code on a file that only contains \r\n\r\n, the read call returns 2 and the buffer contains \n\n. The \r characters are completely ignored. I'm running this on Windows (this wouldn't even be an issue on Linux machines)
Why does read(2) skip over the \r character when it finds it? Or is that what is happening?
EDIT: Conclusion is that windows defaults to opening files in "text" mode as opposed to "binary" mode. For this reason, when calling open, we must specify O_BINARY as the mode.
Thanks, code below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void invertBytes(size_t amount, char* buffer);
int main(int argv, char** argc)
{
int fileCount = 1;
char* fileName;
int fd = 0;
size_t bufSize = 1000;
size_t amountRead = 0;
char* text;
int offset = 0;
if(argv <= 1)
{
printf("Usages: encode [filenames...]\n");
return 0;
}
text = (char *)malloc(sizeof(char) * bufSize);
for(fileCount = 1; fileCount < argv; fileCount++)
{
fileName = argc[fileCount];
fd = open(fileName, O_RDWR);
printf("fd: %d\n", fd);
amountRead = read(fd, (void *)text, bufSize);
printf("Amount read: %d\n", amountRead);
invertBytes(amountRead, text);
offset = (int)lseek(fd, 0, SEEK_SET);
printf("Lseek to %d\n", offset);
offset = write(fd, text, amountRead);
printf("write returned %d\n", offset);
close(fd);
}
return 0;
}
void invertBytes(size_t amount, char* buffer)
{
int byteCount = 0;
printf("amount: %d\n", amount);
for(byteCount = 0; byteCount < amount; byteCount++)
{
printf("%x, ", buffer[byteCount]);
buffer[byteCount] = ~buffer[byteCount];
printf("%x\r\n", buffer[byteCount]);
}
printf("byteCount: %d\n", byteCount);
}
fd = open(fileName, O_RDWR);
should be
fd = open(fileName, O_RDWR | O_BINARY);
See read() only reads a few bytes from file for details.
Try opening with O_BINARY to use binary mode, text mode may be default and may ignore \r.
open(fileName, O_RDWR|O_BINARY);

Resources