Hi salem, thanks for your comment.
The problem wasn't wrinting to a read-only file, but rather creating a new backup without deleting the older backup first.
Anyway, I found an easy solution eventually that seems to work.
Code:
int copy_file(const char* source, const char* folder);
// testing copy_file()
int main(int argc, char *argv[]) {
if (argc!=3) {
printf("arguments!\n");
return 1;
}
char source[PATH_MAX];
char dest_folder[PATH_MAX];
realpath(argv[1], source);
realpath(argv[2], dest_folder);
copy_file(source, dest_folder);
return 0;
}
// both 'source' and 'folder' are guaranteed to be absolute paths
int copy_file(const char* source, const char* folder) {
char copy[PATH_MAX];
strcpy(copy, folder);
strcat(copy, "/");
strcat(copy, strrchr(source, '/')+sizeof(char)); // 'source' is absolute
strcat(copy, "_"); // adding underscore to distinguish it from (possible) existing copy
int source_fd;
int copy_fd;
struct stat source_stat;
if (stat(source, &source_stat)) {
perror("stat");
return 1;
}
if ((source_fd=open(source, O_RDONLY))==-1) {
perror("open");
return 1;
}
if ((copy_fd=open(copy, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode))==-1) { // making sure open() creates the file
perror("open");
close(source_fd);
return 1;
}
char buffer[READ_BLOCK];
int bytes_read;
while ((bytes_read=read(source_fd, buffer, READ_BLOCK))!=0) {
if (bytes_read==-1) {
perror("read");
break;
}
if (write(copy_fd, buffer, bytes_read)==-1) {
perror("write");
break;
}
}
close(source_fd);
close(copy_fd);
char older_backup[PATH_MAX]={0};
strncpy(older_backup, copy, strlen(copy)-1);
// if an older backup exists
if (!access(older_backup, F_OK)) {
if (unlink(older_backup)) {
perror("unlink");
return 1;
}
}
// renaming new backup
if (rename(copy, older_backup)) {
perror("rename");
return 1;
}
return 0;
}