POSIX read() function does not read any bytes when lseek() is called - c

I have the following program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char* argv[]) {
int fd;
char buffer[100];
// open notes file
fd = open("/var/testfile", O_RDONLY);
if(fd == -1) {
error("in main() while opening file for reading");
}
int readBytes = 0;
// read 10 bytes the first time
readBytes = read(fd, buffer, 10);
buffer[10] = 0;
printf("before lseek: %s\n readBytes: %d\n", buffer, readBytes);
// reset buffer
int i = 0;
for(i = 0; i < 10; i++) {
buffer[i] = 0;
}
// go back 10 bytes
lseek(fd, -10, SEEK_CUR);
// read bytes second time
readBytes = read(fd, buffer, 10);
buffer[10] = 0;
printf("after lseek: %s\n readBytes: %d\n", buffer, readBytes);
}
And the following content in the /var/testfile:
This is a test.
A second test line.
The output of the program:
before lseek: This is a
readBytes: 10
after lseek:
readBytes: 0
I don't unerstand why after the lseek() call the read() function does not read any bytes. What is the reason for this? I would expect the same result as I get from the first read() function call.

My compiler says "xxc.c:33:5: warning: implicit declaration of function ‘lseek’ [-Wimplicit-function-declaration]"
This means that the second argument will be assumed to be an integer (probably 32bits), but the definition is actually for the type "off_t" which on Linux or Windows will be a longer 64bit integer.
This means the offset you're giving it is likely to be VERY large and well past the end of your testfile.
The manual says that for lseek() you need the headers:
#include <sys/types.h>
#include <unistd.h>

Related

Reading a Binary File for Floats and Putting them into an Array

I am reading a binary file for floats and storing them in an array. There is a set of 4 byte floating-point values in the file. However, with my current code, the values of the last two indexes of the array are always read as the same value instead of what the actual value of the last index is.
For example, I am getting
array[0] = -123456.123456
array[1] = 123456.123456
array[2] = 123456.123456
when I should be getting something like
array[0] = -123456.123456
array[1] = 123456.123456
array[2] = 654321.654321
I am not sure what I am doing incorrectly in terms of the reading and why I am getting this output.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <float.h>
int main(int argc, char *argv[]) {
int fd;
float num;
size_t nr;
int elementnum = 0;
fd = open(argv[1], O_RDONLY);
nr = read(fd, &num, sizeof(float));
if (fd == -1){
printf("Error opening file");
exit(1);
}
if (nr == -1){
printf("Error reading file");
exit(1);
}
struct stat st;
fstat(fd, &st);
off_t size = st.st_size;
for (int j = 0; j < size/4; j++){
elementnum++;
}
printf("number of elements: %d\n", elementnum);
float array[elementnum];
for (int i = 0; i < elementnum; i++){
nr = read(fd, &num, sizeof(float));
array[i] = num;
}
for (int i = 0; i < elementnum; i++){
printf("Before Sorting: %f\n", array[i]);
}
close(fd);
return 0;
}
You need to check the value returned by each read. Since you read one byte out of the file right after you open it (apparently just to validate that one read works) and do not rewind, your loop is trying to read one more value than is available. If you check the read on the final iteration, I suspect you will see that it returns zero. Since it returns 0, it does not change the value of num, so num retains the value it had on the penultimate iteration.
IOW, stop reading a byte after you open. Instead, just open the file and check that open was successful. Then read until read returns 0 or -1. If read returns 0 before you expect it or if it returns -1, then print an error message.

C program of accessing device file don't work

I have seen device file can be accessed directly in Linux and I want to have a try. I have a free disk partition without any file system. My test code is below.
I expect to get output read data: 199 when I run the program at the second time. But actually, I get output read data: 0 twice. No errors emerge during the program. I have no idea where is wrong.
Thanks for your time.
Test Code:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(){
int num = 0;
int fd = open("/dev/sda6", O_RDWR);
if(fd == -1){
fprintf(stderr, "open device failed, errno : %s(%d) \n",
strerror(errno), errno);
return 1;
}
ssize_t ret = read(fd, &num, sizeof(int));
if(ret != sizeof(int)){
fprintf(stderr, "read fails, errno : %s(%d) \n",
strerror(errno), errno);
return 1;
}
printf("read data: %d\n", num);
num = 199;
ret = write(fd, &num, sizeof(int));
if(ret != sizeof(int)){
fprintf(stderr, "write fails, errno : %s(%d) \n",
strerror(errno), errno);
return 1;
}
close(fd);
return 0;
}
The read and write start reading/writing at the implicit file offset stored in the descriptor, and increment it by the number of bytes read/written. Therefore, you would now read bytes 0 .. 3, and then write bytes 4 .. 7.
Instead of read and write and messing with lseek et al, use the POSIX standard pread and pwrite that do not use the implicit file offset in the descriptor but take explicit file offsets from the beginning of a file in the call.
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
...
ssize_t ret = pread(fd, &num, sizeof(int), 0);
ssize_t ret = pwrite(fd, &num, sizeof(int), 0);
You do not seek in your program, so what it does:
Read the first 4 bytes of the device, then write the second 4 bytes.
Try
lseek(fd,0,SEEK_SET);
before write if you want to write at the beginning of fole.

mmap is wiping my file instead of copying it

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.

write file by mmap, but when I use fread, the second time read error data

When I use mmap and memcpy to write a file, and then I use fread to read the data.
Below is my code, The problem is the first time i can read the a, but the second time i can't read a.
I guess there is something like seek position in fread function, when I use memcpy to write file, It may change the seek position.
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
int main()
{
int fd = open("./aa", O_CREAT | O_RDWR | O_TRUNC, 0644);
FILE* f = fopen("./aa", "r");
if (ftruncate(fd, 1024) < 0) {
printf("ftruncate error\n");
}
void* base;
if ((base = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
printf("mmap error\n");
}
char* file_ptr = (char *)base;
char buffer[256];
char scratch[256];
buffer[0] = 'a';
memcpy(file_ptr, buffer, 1);
file_ptr += 1;
size_t n = fread(scratch, 1, 1, f);
printf("size n %zu\n", n); // this output size n 1
printf("scratch %c\n", scratch[0]); // this output scratch a
memcpy(file_ptr, buffer, 1);
file_ptr += 1;
n = fread(scratch, 1, 1, f);
printf("size n %zu\n", n); // this output size n 1
printf("scratch %c\n", scratch[0]); // but this output scratch
return 0;
}
The output is :
size n 1
scratch a
size n 1
scratch
First of all, #wildplasser is right, your program may work, but if you go on mixing mmap and stdio you'll need to make sure that writes done via mmap get committed (use the msync() function) and that fread isn't buffering stale data (fseek()ing to the current position should do the trick).
Coming to your question: your program doesn't print "scratch", it prints "scratch \0" :)
Seriously, what you do is initialize the size of the "aa" file via ftruncate(), which is the same as filling the missing bytes up to 1024 '\0'; you write an 'a', and read it; then you read another character, and you get one of the NULs.
Try printing the ascii character of scratch[0] and you'll see it's zero; if you're still not convinced, try adding something like
for(i = 0; i < 6; i++)
file_ptr[i] = "QWERTY"[i];
right before the first memcpy and see what happens.

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