A simple sendfile program, but not works - c

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int fd1,fd2,rc;
off_t offset = 0;
struct stat stat_buf;
fd1=open("./hello.txt",O_RDONLY); //read only
fd2=open("../",O_RDWR); //both read and write
fstat(fd1, &stat_buf); //get the size of hello.txt
printf("file size: %d\n",(int)stat_buf.st_size);
rc=sendfile (fd2, fd1, &offset, stat_buf.st_size);
}
So as you have seen, it's quite a simple program. But I just can't find hello.txt in ../
My aim is to see what happens if I put a whatever number, says 10, instead of st_size which may be hundreds of bytes.
Edit:
Thanks for your answers. Well, I followed your advice and changed
fd2=open("../",O_RDWR);
to
fd2=open("../hello.txt",O_RDWR);
Also, I checked the return value of fstat and sendfile, everything is ok.
But the problem is still the same.

You need to specify the filename in the second open, not just the directory name.
Please be sure to check the return values of all these functions, including fstat.

Have you tried fd2 = open("../hello.txt",O_RDWR);?

1>
fd1=open("./hello.txt",O_RDONLY); //read only
fd2=open("../",O_RDWR); //both read and write
replace with
fd1=open("../hello.txt",O_RDONLY); //read only
fd2=open("../your_file_name",O_RDWR);
2>
fstat(fd1, &stat_buf);
will fill up some info related to fd1 file in stat_buf . Here size of that file is also return in that structure with st_size element.
now in
rc=sendfile (fd2, fd1, &offset, stat_buf.st_size);
total stat_buf.st_size bytes are going to send on fd2 file. if here if you write 10 then only 10 bytes will go in fd2.

Related

How to read file permission bits using only the open and read system calls?

I can examine a file's permission bits using the stat() system call, which returns a struct, which contains a field that in turn contains the file type and mode. Is there a way to do the same using nothing but the open and read syscalls? I.e. by analyzing each bit? For example the following code reads a file (the first four bytes) and determines whether it's an ELF file or not ..
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = open("main", O_RDONLY);
char *buf = malloc(sizeof (char) * 4);
read (fd, buf, 4);
if (strcmp(buf, "ELF"))
printf("It is an ELF file.\n");
free(buf);
return 0;
}
Is there a similar way to read a file to extract the information bit-by-bit?
File permissions are not part of the file's contents but part of its directory entry, therefore you can't read the permissions using open or read on the file.
Using stat is the proper way to do this.
I saw that you mentioned in another comment that you're doing this for learning purposes only. Anyone else reading this for production work...DONT. It'll be non-portable! You probably just want to use stat on the containing directory.
You're going to want to take a look at your systems definition of the stat function. Here is one example of the stat function implementation. Its definitely not as easy as just calling stat. But if you study this source and follow links in it, you'll get an idea of how it works.
Unfortunately I'm sane enough to not study the source, and am unsure if it can be done with just combinations of open and read. My guess is no, though (just a guess)

Reading File in C using System Call

I have tried several solution answers from the site but unable to understand what is going wrong with this code.
I am simply trying to read the file data.txt and print it. The file contains just 12 characters "abcd1234efgh".
fd comes out positive but "br" is 0 on executing the read. Please help out if anyone has some clue on this
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
int main(int args,char* vargs[])
{
int fd = 0;
fd = open("data.txt",O_RDONLY);
if(fd<=0)
printf("Invalid file name");
else{
off_t fs =lseek(fd, (off_t) 0, SEEK_END);
char buf[10];
off_t br = read(fd,buf,10);
printf("%s",buf);
}
return 0;
}
lseek(fd, 0, SEEK_END);
After this file pointer is set at the end of the file, so any further reading is unsuccessful. Just comment this instruction out, or change it to suit your needs.
This:
lseek(fd, (off_t) 0, SEEK_END);
seeks to (an offset of 0 from) the end of the file. When you subsequently try to read, there are no bytes available past that point. You should not need to seek at all if you want to read from the beginning of the file.

Reading from files

Yesterday in my class we started working with files for the first time. I wanted to see how it works, so I made a program where I write a word and that word has to be written in a certain file(that part worked). After that, I wanted to read from that file a number of characters and show it on the screen.
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <sys/stat.h>
#include <process.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int df,m;
char c[50],d[50];
printf("c= \n");
gets(c);
m=strlen(c);
df=open("e:\\codeblocks\\fisperimente\\text2.txt",O_RDONLY|O_WRONLY);
if (df==-1) {printf("error");exit(1);}
write(df,c,m);
/*int i,n;
n=read(df,d,5);
for (i=1;i<=n;i++)
{
printf("%c",d[i]);
} */
close(df);
return 0;
}
What I put in my commentary is the part that doesn`t work. I noticed that if I printf n, it returns -1, which means that I did something wrong while reading from the file.
First, I would avoid gets() at all costs. It is unsafe, and is considered deprecated. It may be removed in the future. Instead, consider using fgets(), like so:
fgets(c, sizeof(c), stdin);
Next, your open is suspicious:
df=open("e:\\codeblocks\\fisperimente\\text2.txt",O_RDONLY|O_WRONLY);
Read only and write only are mutually exclusive. If you want to open a file for both read and write, you need O_RDWR.
And finally, after you write to your file, the file pointer points to the end of file. If you want to reread it, you need to seek back to the beginning. This will do that:
lseek(df, 0, SEEK_SET);
Once you do that, you can read your file back in.
You need to seek to the beginning of the file, to read it's contents back. Or you can just close it or open it again for reading.
Try use flag O_RDWR, the or'd result of read only and write only flags is not what you think it does ;)
Note that mode O_RDWR is not usually equal to O_RDONLY | O_WRONLY. Therefore, you probably opened the file with O_WRONLY (classically, O_RDONLY is 0). This is not yet your major problem; it will become one.

"Inappropriate file type or format"

I am trying to add a new file member with ar_hdr format and put it right after the last element in an archive. My code compiles, but when I want to view the file name with the ar -t command, I got an error message that says: ar: hello.a: Inappropriate file type or format. Can someone take a look at my code and give me some hints on how to fix it? Thanks.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
#define BLOCKSIZE 1
int main (int argc, char **argv)
{
char *archive = argv[1];
char *read_file = argv[2];
int in_fd;
int out_fd;
char title[] = ARMAG; //constant define in ar.h
char buf[BLOCKSIZE];
int num_read;
int num_written;
struct stat stat_file;
struct ar_hdr my_ar;
//open read_file (i.e., text file)
if (stat(read_file, &stat_file) == -1){
perror("Error in Stat");
exit(-1);
}
//assign file info to struct dhr (my_ar)
sprintf(my_ar.ar_name, "%s", read_file);
sprintf(my_ar.ar_date, "%ld", stat_file.st_mtimespec.tv_sec);
sprintf(my_ar.ar_uid, "%i", stat_file.st_uid);
sprintf(my_ar.ar_gid, "%i", stat_file.st_gid);
sprintf(my_ar.ar_mode, "%o", stat_file.st_mode) ;
sprintf(my_ar.ar_size, "%lld", stat_file.st_size) ;
//0666 - open archive
out_fd = open(archive, O_CREAT | O_WRONLY | O_APPEND, 0666);
if (out_fd == -1) {
perror("Canot open/create output file");
exit(-1);
}
//write my_ar to archive
num_written = write(out_fd, title, sizeof(title));
num_written = write(out_fd, &my_ar, sizeof(my_ar));
printf("\n");
return 0;
}
The title should only appear once in the file, at the very beginning. If you're appending a file to an archive, you just need to write the ar_hdr for the new file, followed by the contents of the file.
So you need to check whether the file already exists. If it doesn't, and you're creating a new archive, you need to write title first. If it does exist, skip that step. Since you're opening the file in append mode, you can tell whether it's a new file using lseek():
off_t curpos = lseek(out_fd, SEEK_CUR, 0); // Get current position
if (curpos == 0) { // At beginning, it must be a new file
num_written = write(out_fd, ARMAG, SARMAG);
}
user2203774, I'm going to re-iterate: You're working with a poorly-documented file format that is not standardized. The only way to know the correct file format is to (a) study the documentation links I gave in your other question, and (b) analyze a sampling of the archive files you have, to determine whether or not the format conforms to such documentation as you have. The likelihood is that the file format will be similar, but not identical. THEN, you need to figure out what the differences are. Once you do that, create your own file format document, and work off of that.
ALSO NOTE that the first and simplest debugging step is "compare what my program generated with the equivalent as generated by ar. How are they different? Why?" (The od -c command is helpful here, or a side-by-side hex viewer).
Several points:
The magic header string ("!<arch>\n") only goes at the start of the file, not at the beginning of every record.
All the fields in the ar_hdr structure are filled with spaces, and are not null-terminated strings.
Unless you're prepared to update the symbol table (including adding one if necessary) and rewrite the entire file, you must make sure your filenames do not exceed fifteen characters.
Each filename must be immediately followed by a / character (which must not be followed by a null. Again, the rest of the field filled with spaces.)
You're never initializing the ar_fmag field of the ar_hdr structure. This needs to be initialized to a specific value.
After you write the header, you need to write the actual content of the file.
Some references indicate that when the file content is written, you must write an even number of bytes. If the file size is an odd number, a single \n should be appended as padding (and the file size as recorded in the header is NOT incremented to include the padding byte).

How can I use Linux's splice() function to copy a file to another file?

here's another question about splice(). I'm hoping to use it to copy files, and am trying to use two splice calls joined by a pipe like the example on splice's Wikipedia page. I wrote a simple test case which only tries to read the first 32K bytes from one file and write them to another:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
int pipefd[2];
int result;
FILE *in_file;
FILE *out_file;
result = pipe(pipefd);
in_file = fopen(argv[1], "rb");
out_file = fopen(argv[2], "wb");
result = splice(fileno(in_file), 0, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
result = splice(pipefd[0], NULL, fileno(out_file), 0, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
if (result == -1)
printf("%d - %s\n", errno, strerror(errno));
close(pipefd[0]);
close(pipefd[1]);
fclose(in_file);
fclose(out_file);
return 0;
}
When I run this, the input file seems to be read properly, but the second splice call fails with EINVAL. Anybody know what I'm doing wrong here?
Thanks!
From the splice manpage:
EINVAL Target file system doesn't support splicing; target file is
opened in append mode; neither of the descriptors refers to a
pipe; or offset given for non-seekable device.
We know one of the descriptors is a pipe, and the file's not open in append mode. We also know no offset is given (0 is equivalent to NULL - did you mean to pass in a pointer to a zero offset?), so that's not the problem. Therefore, the filesystem you're using doesn't support splicing to files.
What kind of file system(s) are you copying to/from?
Your example runs on my system when both files are on ext3 but fails when I use an external drive (I forget offhand if it is DOS or NTFS). My guess is that one or both of your files are on a file system that splice does not support.
The splice(2) system call is for copying between files and pipes and not between files, so it can not be used to copy between files, as has been pointed out by the other answers.
As of Linux 4.5 however a new copy_file_range(2) system call is available that can copy between files. In the case of NFS it can even cause server side copying.
The linked man page contains a full example program.

Resources