Call system UNIX - File copy in C - c

I try to create a copy of a source file but the target file is always empty.
The algorithm is: read from STDIN and write to source file, then read on this file and write the text in target file.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFSIZE 8192
int main(){
int fdsource, fdtarget;
int n, nr;
char buff[BUFFSIZE];
fdsource = open("source.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // Create and open a source file in read/write
if (fdsource < 0){
printf("Source file open error!\n");
exit(1);
}
fdtarget = open("target.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); // Create and open a source file in write only
if (fdtarget < 0){
printf("Target file open error!\n");
exit(1);
}
printf("\nInsert text:\n");
while ((n = read(STDIN_FILENO, buff, BUFFSIZE)) > 0){ // Read from STDIN and write to source file
if ((write(fdsource, buff, n)) != n){
printf("Source file write error!\n");
exit(1);
}
}
while ((read(fdsource, buff, n)) > 0){ // Read from source file and write to target file
if ((write(fdtarget, buff, n)) != n){
printf("Source file open error!\n");
exit(1);
}
}
close(fdsource);
close(fdtarget);
exit(0);
return 0;
}

The problem with your code is "You have opened both the file in initial stage". To solve the problem just open the source file in the write mode and write all the data, then close and reopen the source file in read mode, Then open the target file in the write mode.
The modified code is given below and it was not tested
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFSIZE 8192
int main(){
int fdsource, fdtarget;
int n;
char buff[BUFFSIZE];
fdsource = open("source.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // Create and open a source file in read/write
if (fdsource < 0){
printf("Source file open error!\n");
exit(1);
}
printf("\nInsert text:\n");
while ((n = read(STDIN_FILENO, buff, BUFFSIZE)) > 0){ // Read from STDIN and write to source file
if ((write(fdsource, buff, n)) != n){
printf("Source file write error!\n");
exit(1);
}
}
close(fdsource);
fdsource = open("source.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // Create and open a source file in read/write
if (fdsource < 0){
printf("Source file open error!\n");
exit(1);
}
fdtarget = open("target.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); // Create and open a source file in write only
if (fdtarget < 0){
printf("Target file open error!\n");
exit(1);
}
while ((read(fdsource, buff, n)) > 0){ // Read from source file and write to target file
if ((write(fdtarget, buff, n)) != n){
printf("Source file open error!\n");
exit(1);
}
}
close(fdsource);
close(fdtarget);
exit(0);
return 0;
}
If am wrong anywhere use the logic mentioned above.

Related

My program for copying one file to another fails

I was trying to copy what is written in a file to a different file (with system calls) but my code seems not to work. I have first tried just printing with printf() the buffer but it also does not work. My guess is that I'm reading the file incorrectly.
#define BUF_SIZE 200
int main(int argc, char *argv[]){
int entrada,salida,leidos;
char buffer[BUF_SIZE];
entrada = open(argv[1],O_RDONLY);
salida = creat(argv[2], 0644);
while( (leidos = read(entrada,buffer,BUF_SIZE)) > 0 ){
write(salida,buffer,leidos);
}
close(salida);
close(entrada);
return 0;
}
What's wrong with my implementation?
I think you're missing the appropriate open flags on the output . Try:
salida = creat(argv[2], O_WRONLY | O_CREAT, 0644);
However, as comments suggest, you're probably getting errors indicated by the return values and/or the errno variable, which you are ignoring.
Also, I would avoid Castellano-specific variable names. Writing C/C++ requires knowing English anyway, so better stick to that for naming; otherwise - people who don't speak Castellano will have trouble understanding your code.
Finally - why are you doing it this way? There are much nicer C++-friendly, or even C-friendly, ways to copy a file - which would also be portable (your code isn't). See:
Copy a file in a sane, safe and efficient way
As #einpoklum stated, the main problem must be probably searched in the way you are opening your output file (flags and permissions). Overall, your code is far from implementing the minimum debugging verbosity and I think that a few code flow controls would help you a lot in detecting the real problem in your code:
#include <stdio.h>
#include <stdlib.h>
#ifndef BUF_SIZE
#define BUF_SIZE 200
#endif
int main(int argc, char *argv[])
{
int entrada = open(argv[1], O_RDONLY);
if (entrada == -1)
{
fprintf(stderr, "Error opening: %s\n", argv[1]);
exit(-1);
}
int openFlags = O_CREAT | O_WRONLY | O_TRUNC;
mode_t filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int salida = open(argv[2], openFlags, filePerms);
if (salida == -1)
{
fprintf(stderr, "Error opening: %s\n", argv[2]);
exit(-1);
}
ssize_t numRead;
char buf[BUF_SIZE];
while ((numRead = read(entrada, buf, BUF_SIZE)) > 0)
{
if (write(salida, buf, numRead) != numRead)
{
fprintf(stderr, "Writing error!\n");
exit(-1);
}
}
if (numRead == -1)
{
fprintf(stderr, "Reading error!\n");
exit(-1);
}
if (close(entrada) == -1)
{
fprintf(stderr, "Input closing error!\n");
exit(-1);
}
if (close(salida) == -1)
{
fprintf(stderr, "Output closing error!\n");
exit(-1);
}
exit(0);
}
Many of the functions you use return values that can provide you an insight on that's going on. Use them.

create new file with system calls

Im trying to create a new file / overwrite an existing file using systemcalls , but for some reason I have two problems:
1. When I'm first running the program it exits with value 0, so it seems like it created the file successfully, but I can't see anything in my project directory.
then when I secondly running the program the file is created, but an error message is printed on the screen.
2. Also after the first iteration of the program, I can't see the prinf message at the end of the main function.
Thanks for helping.
int readFileDesc = 0, writeFiledesc = 0;
int sourceFile = 1, destFile = 2, bufferSize = 3, isOverwrite;
if (argc != 4 && argc != 5) {
printf("Invalid number of arguments\n");
printf("Usage:\n");
printf(" ex1 [-f] SOURCE DEST BUFFER_SIZE");
exit(EXIT_FAILURE);
}
//Checking if -f [OP] is activated.
isOverwrite = (strcmp(argv[1], "-f") == 0);
if (isOverwrite) {
sourceFile++;
destFile++;
bufferSize++;
}
//Opening the source file
readFileDesc = open(argv[sourceFile], O_RDONLY);
if (readFileDesc < 0) {
perror("Unable to open source file for reading: ");
exit(EXIT_FAILURE);
}
//opening the destination file
if (!isOverwrite) {
//Case we dont have the -f [op] so we create the file.
writeFiledesc = open(argv[destFile],
O_CREAT | O_EXCL | O_WRONLY ,
S_IRUSR | S_IWUSR);
if (writeFiledesc < 0) {
perror("Unable to open destination file for reading: ");
exit(EXIT_FAILURE);
}
} else {
//Case we have the -f [op] so we override existing file.
writeFiledesc = open(argv[destFile], O_RDONLY | O_WRONLY | O_TRUNC);
if (writeFiledesc < 0) {
perror("Unable to open destination file for writing: ");
exit(EXIT_FAILURE);
}
}
//Assume the buffersize is legal.
bufferSize = atoi(argv[bufferSize]);
char data[bufferSize];
int nread, nwrite;
while ((nread = read(readFileDesc, data, bufferSize)) > 0) {
if ((nwrite = write(writeFiledesc, data, nread)) != nread) {
printf("write problem: ");
}
}
// cant see this!
printf("File %s was copied to %s" , argv[sourceFile] , argv[destFile]);
//handling errors
close(sourceFile);
close(destFile);
return EXIT_SUCCESS;
}
This is wrong:
writeFiledesc = open(argv[destFile], O_RDONLY | O_WRONLY | O_TRUNC);
Using both O_RDONLY and O_WRONLY is wrong. You need to use O_RDWR.
Per the POSIX standard for open():
SYNOPSIS
#include <sys/stat.h> #include <fcntl.h>
int open(const char *path, int oflag, ...);
...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in . Applications shall
specify exactly one of the first five values (file access modes)
below in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
Any combination of the following may be used:
...
Also, read() and write() return ssize_t, not int.

Copying file fails, EBADF on closing output file descriptor

So I was following a little outdated book (2010) and I'm trying to copy a file with Linux system calls. This is what i have:
NOTE: Ignore the tlpi_hdr.h and error_functions.h, they define errExit() and fatal() and some otheres, they just print the error and exit.
#include <stdio.h>
#include <fcntl.h>
#include "lib/tlpi_hdr.h"
#include "lib/error_functions.h"
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[])
{
int inputFd, outputFd, openFlags;
mode_t filePerms;
ssize_t numRead;
char buf[BUF_SIZE];
if (argc != 3 || strcmp(argv[1], "--help") == 0) {
usageErr("%s old-file new-file\n", argv[0]);
}
inputFd = open(argv[1], O_RDONLY);
if (inputFd == -1) {
errExit("Opening file %s", argv[1]);
}
openFlags = O_CREAT | O_WRONLY | O_TRUNC;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
outputFd = open(argv[2], openFlags, filePerms);
if (outputFd == -1) {
errExit("Opening file for writing %s", argv[1]);
}
while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0) {
if (write(outputFd, buf, numRead) != numRead))
fatal("I/O Error");
if (numRead == -1)
fatal("Reading error");
}
if (close(outputFd == -1))
errExit("close input");
if (close(inputFd == -1))
errExit("close output");
return EXIT_SUCCESS;
}
I'm failing on closing of the output file descriptor with EBADF Bad file descriptor:
thinkpad :: ~/.tlpi % ./cp.o a b
ERROR [EBADF Bad file descriptor] close output
The file copies fine tho:
thinkpad :: ~/.tlpi % sha1sum a
40a925a93e149ac53d2630cde8adeb63b8134b29 a
thinkpad :: ~/.tlpi % sha1sum b
40a925a93e149ac53d2630cde8adeb63b8134b29 b
thinkpad :: ~/.tlpi %
Why?
Let's take a closer look at your close call:
close(outputFd == -1)
Here you are comparing outputFd to the value -1. The result of that is a boolean value, which in C will be either 0 or 1. This happens to be either standard input or standard output, depending on the result. Not a file you descriptor you should close.
My guess is that you meant
if (close(outputFd) == -1)

Open() for output not creating file

I have this function that utilizes open to set i/o redirection:
void setOutput(char * buffer){
int file = open(buffer, O_WRONLY || O_CREAT, S_IWUSR);
if(file < 0){ printf("error opening %s for output\n", buffer); }
if(dup2(file, 1) < 0){ printf("error with dup2 opening %s for output\n", buffer); }
}
When I run it, it works fine for files that are already defined but returns -1 when it receives a non-created file. Not sure why
You need to change the following
int file = open(buffer, O_WRONLY || O_CREAT, S_IWUSR);
To
int file = open(buffer, O_WRONLY | O_CREAT, S_IWUSR);
Format :
int open( char *filename, int access, int permission );
access : Should be provided as a bit wise OR operator, that means using | not || which is logical OR

Why does mmap() fail with permission denied for the destination file of a file copy program?

I'd like to give a try at copying the contents of a file over to another one by using memory mapped I/O in Linux via mmap(). The intention is to check by myself if that's better than using fread() and fwrite() and how would it deal with big files (like couple of GiBs for example, since the file is read whole I want to know if I need to have such amount of memory for it).
This is the code I'm working with right now:
// Open original file descriptor:
int orig_fd = open(argv[1], O_RDONLY);
// Check if it was really opened:
if (orig_fd == -1) {
fprintf(stderr, "ERROR: File %s couldn't be opened:\n", argv[1]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
exit(EX_NOINPUT);
}
// Idem for the destination file:
int dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
// Check if it was really opened:
if (dest_fd == -1) {
fprintf(stderr, "ERROR: File %s couldn't be opened:\n", argv[2]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
// Close original file descriptor too:
close(orig_fd);
exit(EX_CANTCREAT);
}
// Acquire file size:
struct stat info = {0};
if (fstat(orig_fd, &info)) {
fprintf(stderr, "ERROR: Couldn't get info on %s:\n", argv[1]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
// Close file descriptors:
close(orig_fd);
close(dest_fd);
exit(EX_IOERR);
}
// Set destination file size:
if (ftruncate(dest_fd, info.st_size)) {
fprintf(stderr, "ERROR: Unable to set %s file size:\n", argv[2]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
// Close file descriptors:
close(orig_fd);
close(dest_fd);
exit(EX_IOERR);
}
// Map original file and close its descriptor:
char *orig = mmap(NULL, info.st_size, PROT_READ, MAP_PRIVATE, orig_fd, 0);
if (orig == MAP_FAILED) {
fprintf(stderr, "ERROR: Mapping of %s failed:\n", argv[1]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
// Close file descriptors:
close(orig_fd);
close(dest_fd);
exit(EX_IOERR);
}
close(orig_fd);
// Map destination file and close its descriptor:
char *dest = mmap(NULL, info.st_size, PROT_WRITE, MAP_SHARED, dest_fd, 0);
if (dest == MAP_FAILED) {
fprintf(stderr, "ERROR: Mapping of %s failed:\n", argv[2]);
fprintf(stderr, "%d - %s\n", errno, strerror(errno));
// Close file descriptors and unmap first file:
munmap(orig, info.st_size);
close(dest_fd);
exit(EX_IOERR);
}
close(dest_fd);
// Copy file contents:
int i = info.st_size;
char *read_ptr = orig, *write_ptr = dest;
while (--i) {
*write_ptr++ = *read_ptr++;
}
// Unmap files:
munmap(orig, info.st_size);
munmap(dest, info.st_size);
I think it may be a way of doing it but I keep getting an error trying to map the destination file, concretely code 13 (permission denied).
I don't have a clue on why is it failing, I can write to that file since the file gets created and all and the file I'm trying to copy is just a couple of KiBs in size.
Can anybody spot the problem? How come I had permission to map the original file but not the destination one?
NOTE: If anyone is to use the loop to copy bytes posted in the question instead of memcpy for example, the loop condition should be i-- instead to copy all contents. Thanks to jxh for spotting that.
From the mmap() man page:
EACCES A file descriptor refers to a non-regular file. Or MAP_PRIVATE
was requested, but fd is not open for reading. Or MAP_SHARED
was requested and PROT_WRITE is set, but fd is not open in
read/write (O_RDWR) mode. Or PROT_WRITE is set, but the file is
append-only.
You are opening your destination file with O_WRONLY. Use O_RDWR instead.
Also, you should use memcpy to copy the memory rather than using your own loop:
memcpy(dest, orig, info.st_size);
Your loop has an off by 1 bug.
This works for me. Note that I had to open the destination O_RDWR. I suspect the kernel attempts to map whole pages from the file into memory (reading it) because you're updating it a byte or word at a time, and that might not change the whole page.
A couple of other points:
You don't need to close and unmap stuff on error if you're just going to exit.
Use memcpy and don't write your own byte-copying loop. Memcpy will be a lot better optimised in general. (Though it's not always the absolute best.)
You might want to read the source code to FreeBSD's "cp" utility. Take a look here and search for the use of mmap. http://svnweb.freebsd.org/base/stable/9/bin/cp/utils.c?revision=225736&view=markup
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int s, d;
struct stat st;
void *sp, *dp;
s = open(argv[1], O_RDONLY);
if (s == -1) {
perror("open source");
exit(1);
}
d = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
if (d == -1) {
perror("open destintation");
exit(1);
}
if (fstat(s, &st)) {
perror("stat source");
exit(1);
}
if (ftruncate(d, st.st_size)) {
perror("truncate destination");
exit(1);
}
sp = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, s, 0);
if (sp == MAP_FAILED) {
perror("map source");
exit(1);
}
dp = mmap(NULL, st.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, d, 0);
if (dp == MAP_FAILED) {
perror("map destintation");
exit(1);
}
memcpy(dp, sp, st.st_size);
return 0;
}
Original File: O_RDONLY open, MAP_PRIVATE mmap
destination file: O_WRONLY open, MAP_SHARED mmap
You need to open with O_RDWR flag for using MAP_SHARED.
Don't you actually need to do MAP_FILE | MAP_SHARED ?

Resources