user1491122
user1491122

Reputation: 21

Call read() returns 0 but buffer changed, not happened when call fread() to read the same offset

if I read file by calling read() like this:

unsigned char buf[512];
memset(buf, 0, sizeof(unsigned char) * 512);
int fd;
int readcount;
int offset = 10315001; /* file size is 14315504 */

fd = open("myfile", O_RDONLY);
lseek(fd, offset, SEEK_SET);
readcount = read(fd, (void*)buf, 8);
close(fd);

the read() returns 0, but the memory of "buf" has been changed(not 0 any more). And if I tried to read the same offset and same file with fread() like this:

FILE* file;
file = fopen("myfile", "r");
fseek(file, offset, SEEK_SET);
readcount = fread((void*)buf, 8, 1, file);
fclose(file);

fread() returns 0 either, but the buf is as same as before.

if read() failed, why it changes the memory space of "buf"? Or did I make some mistakes?

Thanks for help : )

Edit: Every time I ran the code above, the "buf" changed by read() in the same way -> from 0 to the same values. So the changed "buf" may not be random values?

Edit 2: The offset parameter is valid(thanks twalberg), and if I read another valid offset, both read() and fread() will be succeeded and the result of "buf" is the same. Is there any way to find what's wrong when read() failed? The errno is "No errors" when read() returns 0.

Upvotes: 1

Views: 1612

Answers (2)

user1491122
user1491122

Reputation: 21

I think I've found what's going on here. The file is a binary one, but I read() it with text mode(O_RDONLY).

The value of offset 10315001 is 0x1a, when read() and fread() function meets 0x1a in text mode, they both will return 0, but the different is, the read() will still write the buf with binary mode, while fread() won't do this.

Upvotes: 1

Jonathan Grynspan
Jonathan Grynspan

Reputation: 43472

The contents of the buffer after an unsuccessful call to read() or a successful zero-byte read() are undefined.

Likely what happened is that it allocated an internal (possibly kernel-side) buffer for temporary storage (which got filled with different garbage) and copied the buffer into your buffer, but didn't actually write to its buffer.

Since you only need to examine the buffer after a successful read, this shouldn't matter. If you have important data in that buffer, move it out of there before passing it to a function that might erase that data!

Edit: The code could look like this. Imagine kernel_read() is the syscall to read from a file descriptor, and it takes a buffer that was allocated in the kernel's address space rather than the process' address space (because kernels do seemingly strange things like that.)

extern __kernel void *kernel_malloc(size_t size);
extern void kernel_copy_from_kernel_to_userland(void *dest, __kernel void *src, size_t size);
extern void kernel_free(__kernel void *address);

extern int kernel_is_valid_fd(int fd);
extern ssize_t kernel_read(int fd, __kernel void *kbuf, size_t count);

ssize_t read(int fd, void *buf, size_t count) {
    ssize_t result = -1;

    if (0 == kernel_is_valid_fd(fd)) {
        __kernel void *kernelbuf = kernel_malloc(count);
        if (kernelbuf) {
            result = kernel_read(fd, kernelbuf, count);
            kernel_copy_from_kernel_to_userland(buf, kernelbuf, count);
            kernel_free(kernelbuf);
        } else {
            errno = ENOMEM;
        }
    } else {
        errno = EINVAL;
    }

    return result;
}

This is a thought experiment, not a real implementation from any shipping operating system, but perhaps it can help you understand why you might have seen what you saw.

Upvotes: 2

Related Questions