Reading binary in C without fopen - c

I have a school project. I have to write a basic virtual machine in C that is able to host a CoreWar game. I am supposed to read from a file written in binary, but I am not allowed to use fopen, fread or fseek.
I have to use read, write and lseek.
I really dont understand how I am supposed to do this, everything I found on internet says I have to use fopen with the "rb" mode.

Here's a complete example of reading the file using the low-level functions you are required to use.
Replace the comment /* Process the data */ with your own code that does something useful with the data you read.
int rfd; /* File descriptor. */
char buffer[BUFFER_SIZE]; /* Buffer to put file content into */
int bufferChars; /* number of characters returned by the read function */
/* Open the file */
if ((rfd = open(argv[1], O_RDONLY, 0)) < 0)
perror("Open failed.");
/* Read and process the file */
while (1)
{
/* Normal case --- some number of bytes read. */
if ((bufferChars = read(rfd, buffer, BUFFER_SIZE)) > 0)
{
/* Process the data */
}
else if (bufferChars == 0) /* EOF reached. */
break;
else /* bufferChars < 0 --- read failure. */
perror("Read failed.");
}
close(rfd);

you might consider using mmap() for reading the file data. Check this answer here: When should I use mmap for file access?

Related

Can I use read from unistd.h to read from file into a struct?

I am trying to do two things: read from a file descriptor into a struct and then I need to read into a memory location. For the first, I am using fread. For the second I am using read from unistd.h.
Is there anyway I can do both without needing to fopen, fclose, open, close and repeating like this? Am I able to use "read" to read into a struct?
After calling fopen(), you may get the file descriptor of a file pointer via fileno(), like this:
int fd;
fd = fileno (fp);
Then you may use read directly. Indeed, both fopen() and open() opens a file for a process, the former returns a pointer to the file while the latter returns the file descriptor. The FILE type is just a struct which has a field that records the underlying file descriptor.
In other words, on unix-like systems, fopen() is implemented using open(), so there must be a way to use both calls simultaneously.
As a note, on my system (wsl2), the definition of FILE can be found under the path /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h, which looks something like this:
struct _IO_FILE
{
/* ... */
int _fileno;
/* ... */
};
Yes, of course you can read data (here a string) into a struct:
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define DATA_LEN 1024
struct s {
char data[DATA_LEN];
};
int main() {
int fd = open("1.c", O_RDONLY);
if(fd == -1) {
perror("file");
return 1;
}
struct s s;
ssize_t n = read(fd, s.data, DATA_LEN);
printf("%.*s\n", (int) n, s.data);
close(fd);
}
Re: Is there anyway I can do both without needing to fopen, fclose,
open, close and repeating like this?
Yes. You can obtain a file descriptor corresponding to the FILE * with fileno(). From the man page:
The function fileno() examines the argument stream and returns
the integer file descriptor used to implement this stream. The
file descriptor is still owned by stream and will be closed when
fclose(3) is called.
As an example:
/**
* #brief Opens the file whose name is the string pointed to
* by file_name and associates a stream with it.
* #return A file descriptor corresponding to the stream on success,
* -1 elsewise.
*/
static int open_sesame (FILE *stream, const char *file_name)
{
stream = fopen (file_name, "r");
if (!stream) {
perror ("fopen()");
/* Handle error here */
return -1;
}
/* On failure, -1 is returned and errno is set to indicate
* the error.
*/
int fd = fileno (stream);
if (fd == -1) {
perror ("fileno()");
/* Handle error here */
return -1;
}
return fd;
}
This too will do:
/* read() will return -1 and set errno to EBADF¹
* if fileno() fails.
*/
if (read (fileno (fp), ..., ..., ...) == -1) {
perror ("read()");
/* Handle error here */
}
On the other hand, if you want to open a stream for a corresponding file descriptor, then there's fdopen(). From the man page:
The fdopen() function shall associate a stream with a file
descriptor.
A sample call:
/* Upon successful completion, fdopen() shall return
* a pointer to a stream; otherwise, a null pointer
* shall be returned and errno set to indicate the
* error.
*/
FILE *stream = fdopen (fd, "r");
if (!stream) {
perror ("fdopen()");
/* Handle error here */
}
Alternatively, as #Fe203 suggested, if you require two copies of the data, you can use the standard memcpy() instead of reading from the file twice. Or as you're already using POSIX system calls, it might be more efficient to map the file into memory with mmap() and work with it.
[1] — EBADF: fd is not a valid file descriptor or is not open for reading.

Listing directory's files using open() and read() syscalls in POSIX systems

I was wondering how to do this. I have tried several things but nothing seems to work for me. I don't want to use opendir() syscall nor do i want to use readdir() system call. Could you please tell me how to do this because i get garbage values. I want to list files that are inside a folder. I get garbage value stored in the buffer from this code.
char buffer[16];
size_t offset = 0;
size_t bytes_read;
int i;
/* Open the file for reading. */
int fd = open ("testfolder", O_RDONLY);
/* Read from the file, one chunk at a time. Continue until read
“comes up short”, that is, reads less than we asked for.
This indicates that we’ve hit the end of the file. */
do {
/* Read the next line’s worth of bytes. */
bytes_read = read (fd, buffer, 16);
/* Print the offset in the file, followed by the bytes themselves.*/
// printf ("0x%06lx : ", offset);
// for (i = 0; i < bytes_read; ++i)
// printf ("%02x ", buffer[i]);
printf("%s", buffer);
printf ("\n");
/* Keep count of our position in the file. */
// offset += bytes_read;
}
while (bytes_read!=-1);
You can't do this. The kernel only allows open on a directory with special options, and it doesn't allow read on a directory at all. You have to use opendir and readdir.
(Under the hood, opendir calls open with those special options I mentioned, and readdir calls the private system call getdents. See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/opendir.c and https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/readdir.c . Doing this yourself is not recommended.)

Incorrect fprintf results

In the code below, I am trying to read from a socket and store the results in a file.
What actually happens, is that my client sends a GET request to my server for a file.html. My server finds the file and writes the contents of it to the socket. Lastly my client reads the content from thread_fd and recreates the file.
For some reason the recreated file has less content than the original. I have located the problem to be some lines in the end, that are missing. When I use printf("%s", buffer) inside the while loop everything seems fine in STDOUT but my fprintf misses somewhat 3.000 bytes for a file of 81.000 bytes size.
#define MAXSIZE 1000
int bytes_read, thread_fd;
char buffer[MAXSIZE];
FILE* new_file;
memset(buffer, 0, MAXSIZE);
if((new_file = fopen(path, "wb+")) == NULL)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
fprintf(new_file, "%s", buffer);
if(bytes_read < MAXSIZE)
break;
memset(buffer, 0, MAXSIZE);
}
You read binary data from the socket that may or may not contain a \0 byte. When you then fprintf that data the fprintf will stop at the first \0 it encounters. In your case that is 3000 bytes short of the full file. If your file contains no \0 byte the fprintf will simply continue printing the ram contents until it segfaults.
Use write() to write the data back to the file and check for errors. Don't forget to close() the file and check that for errors too.
Your code should/could look like:
int readfile(int thread_fd, char *path)
{
unsigned int bytes_read;
char buffer[MAXSIZE];
int new_file;
if ((new_file = open(path, _O_CREAT|_O_BINARY,_S_IWRITE)) == -1) return -1;
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
if (write(new_file, buffer, bytes_read)!= bytes_read) {
close(new_file);
return -2;
}
}
close(new_file);
return 0;
}
There are a few issues with your code that can cause this.
The most likely cause is this :
if(bytes_read < MAXSIZE)
break;
This ends the loop when read returns less than the requested amount of bytes. This is however perfectly normal behavior, and can happen eg. when not enough bytes are available at the time of the read call (it's reading from a network socket after all). Just let the loop continue as long as read returns a value > 0 (assuming the socket is a blocking socket - if not, you'll also have to check for EAGAIN and EWOULDBLOCK).
Additionally, if the file you're receiving contains binary data, then it's not a good idea to use fprintf with "%s" to write to the target file. This will stop writing as soon as it finds a '\0' byte (which is not uncommon in binary data). Use fwrite instead.
Even if you're receiving text (suggested by the html file extension), it's still not a good idea to use fprintf with "%s", since the received data won't be '\0' terminated.
This worked!
ps: I don't know if I should be doing this, since I am new here, but really there is no reason for negativity. Any question is a good question. Just answer it if you know it. Do not judge it.
#define MAXSIZE 1000
int bytes_read, thread_fd, new_file;
char buffer[MAXSIZE];
memset(buffer, 0, MAXSIZE);
if((new_file = open(path, O_RDONLY | O_WRONLY | O_CREAT)) < 0)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
write(new_file, buffer, bytes_read);
close(new_file);

redirect stdout to file - in C

i've been trying to redirect the output to a file and reading from file instead of stdin by using a function, but it doesnt seem to be working as when i redirect into a file and check to see if a file has been created with the output there is nothing there. what could be wrong with this function.
/* Redirect a standard I/O file descriptor to a file
* Arguments: filename the file to/from which the standard I/O file
* descriptor should be redirected
* flags indicates whether the file should be opened for reading
* or writing
* destfd the standard I/O file descriptor which shall be
* redirected
* Returns: -1 on error, else destfd
*/
int redirect(char *filename, int flags, int destfd){
int ret;
if(flags == 0){
destfd = open(filename,O_RDONLY);
if (destfd < 0){
return -1;
}
ret = dup2(0,destfd);
if(ret < 0){
return -1;
}
close(destfd);
}
if(flags == 1){
destfd = open(filename,O_APPEND|O_WRONLY);
if (destfd < 0){
return -1;
}
ret = dup2(destfd,1);
if(ret < 0){
return -1;
}
close(destfd);
}
return destfd;
}
There are several problems with your code, not least of which the really awful formatting which make it very hard to read.
For example this call to dup2 is backwards - it's replacing the recently opened destfd with a copy of stdin.
ret = dup2(0,destfd);
a then a few lines later you close destfd.
Your if statements could benefit from you learning about else and else if
if(flags == 0) {
// code
} else if(flags == 1) {
// code
}
Really though you could simplify the whole function by treating the flags parameter as the same flags you'd pass to open and have destfd as the file descriptor you want to replace.
int redirect(char *filename,int flags, int destfd)
{
int fd;
fd=open(filename,flags,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
if(dup2(fd,destfd)==-1)
{
close(fd);
fd=-1;
}
else
{
close(fd);
}
}
return fd;
}
Then you could call it like
redirect("output.txt",O_CREAT|O_WRONLY,1); // Redirect stdout
redirect("input.txt",O_RDONLY,0); // Redirect stdin
The os function dup2() should provide what you need (if not references to exactly what you need).
More specifically, you can dup2() the stdin file descriptor to another file descriptor, do other stuff with stdin, and then copy it back when you want.
The dup() function duplicates an open file descriptor. Specifically, it provides an alternate interface to the service provided by the fcntl() function using the F_DUPFD constant command value, with 0 for its third argument. The duplicated file descriptor shares any locks with the original.
On success, dup() returns a new file descriptor that has the following in common with the original:
Same open file (or pipe)
Same file pointer (both file descriptors share one file pointer)
Same access mode (read, write, or read/write)
Everything I said can be found on the manpage of dup

read from file as char array

I am reaing from a file, and when i read, it takes it line by line, and prints it
what i want exactly is i want an array of char holding all the chars in the file and print it once,
this is the code i have
if(strcmp(str[0],"#")==0)
{
FILE *filecomand;
//char fname[40];
char line[100];
int lcount;
///* Read in the filename */
//printf("Enter the name of a ascii file: ");
//fgets(History.txt, sizeof(fname), stdin);
/* Open the file. If NULL is returned there was an error */
if((filecomand = fopen(str[1], "r")) == NULL)
{
printf("Error Opening File.\n");
//exit(1);
}
lcount=0;
int i=0;
while( fgets(line, sizeof(line), filecomand) != NULL ) {
/* Get each line from the infile */
//lcount++;
/* print the line number and data */
//printf("%s", line);
}
fclose(filecomand); /* Close the file */
You need to determine the size of the file. Once you have that, you can allocate an array large enough and read it in a single go.
There are two ways to determine the size of the file.
Using fstat:
struct stat stbuffer;
if (fstat(fileno(filecommand), &stbuffer) != -1)
{
// file size is in stbuffer.st_size;
}
With fseek and ftell:
if (fseek(fp, 0, SEEK_END) == 0)
{
long size = ftell(fp)
if (size != -1)
{
// succesfully got size
}
// Go back to start of file
fseek(fp, 0, SEEK_SET);
}
Another solution would be to map the entire file to the memory and then treat it as a char array.
Under windows MapViewOfFile, and under unix mmap.
Once you mapped the file (plenty of examples), you get a pointer to the file's beginning in the memory. Cast it to char[].
Since you can't assume how big the file is, you need to determine the size and then dynamically allocate a buffer.
I won't post the code, but here's the general scheme. Use fseek() to navigate to the end of file, ftell() to get size of the file, and fseek() again to move the start of the file. Allocate a char buffer with malloc() using the size you found. The use fread() to read the file into the buffer. When you are done with the buffer, free() it.
Use a different open. i.e.
fd = open(str[1], O_RDONLY|O_BINARY) /* O_BINARY for MS */
The read statement would be for a buffer of bytes.
count = read(fd,buf, bytecount)
This will do a binary open and read on the file.

Resources