I have a program that should copy files from one folder to another. But the function that reads the files is being blocked when the file have 0 bytes. When I do CTRL+C de program continues and copies all the remaining files which are not 0 bytes. In main.c I have a simple fork to call read and write function according to parent and child processes. All files are correctly copied but it always stops when file is 0 bytes. The read function is:
int read_file(char *ficheiro) {
FILE *fp = fopen(ficheiro, "rb");
if (fp == NULL) {
exit_on_error(-1, "fopen falhou");
}
MSG_STRUCT msg;
int total_bytes = 0, bytes_lidos;
while ((bytes_lidos = fread(msg.buffer, 1, TAMANHO_BUFFER, fp)) > 0) {
msg.type = 1;
msg.bytes_lidos = bytes_lidos;
if (msgsnd(msg_id, &msg, sizeof(MSG_STRUCT) - sizeof(long), 0) == -1) {
perror("Erro ao enviar mensagem");
fclose(fp);
exit(1);
}
total_bytes += bytes_lidos;
}
// Enviar mensagem de finalização, fora do loop
msg.bytes_lidos = 0;
if (msgsnd(msg_id, &msg, sizeof(MSG_STRUCT) - sizeof(long), 0) == -1) {
perror("Erro ao enviar mensagem de finalização");
fclose(fp);
exit(1);
}
fclose(fp);
return total_bytes;
}
And the write function is:
int write_file(char *ficheiro) {
FILE *fp = fopen(ficheiro, "wb");
if (fp == NULL) {
exit_on_error(-1, "fopen falhou");
}
MSG_STRUCT msg;
int total_bytes = 0;
while (1) {
if (msgrcv(msg_id, &msg, sizeof(MSG_STRUCT) - sizeof(long), 1, 0) == -1) {
perror("Erro ao receber mensagem");
fclose(fp);
exit(1);
}
if (msg.bytes_lidos == 0) {
printf("msg.bytes == 0\n");
break;
}
fwrite(msg.buffer, 1, msg.bytes_lidos, fp);
total_bytes += msg.bytes_lidos;
}
fclose(fp);
return total_bytes;
}
In main I have:
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "Número de argumentos inválido\n");
fprintf(stderr, "Sintaxe: %s <origem> <destino>\n", argv[0]);
exit(-1);
}
msg_id = cria_msg(MSG_KEY);
int status;
if (fork() != 0) {
read_file(argv[1]);
wait(&status);
} else {
write_file(argv[2]);
exit(0);
}
remove_msg(MSG_KEY);
return 0;
}
I expect all files to be copied from one folder to another, even if the file is empty.
The Bash file is
#!/bin/bash
CP_CMD=cp-msg
TESTE_DIR=teste
OUTPUT_DIR=output
if [ $# == 2 ]
then
TESTE_DIR=$1
OUTPUT_DIR=$2
fi
if [ ! -d $TESTE_DIR ]
then
echo "Não existe pasta $TESTE_DIR"
exit 1
fi
if [ ! -d $OUTPUT_DIR ]
then
echo "Criar pasta $OUTPUT_DIR"
mkdir $OUTPUT_DIR
fi
rm -f $OUTPUT_DIR/*
for ficheiro in $TESTE_DIR/*
do
nome=`basename $ficheiro`
tamanho_ficheiro=$(stat -c%s "$ficheiro")
echo -n "Copiar $ficheiro ($tamanho_ficheiro bytes) para $OUTPUT_DIR/$nome: "
/usr/bin/time -f "em %E" ./$CP_CMD $ficheiro $OUTPUT_DIR/$nome
echo -n "Verificar cópia: "
diff $ficheiro $OUTPUT_DIR/$nome >/dev/null
if [ $? != 0 ]
then
echo "ficheiros diferentes"
else
echo "ficheiros iguais"
fi
echo
done
Why is fread being blocked when reading and copying a 0 bytes file?
It is highly unlikely that that is happening. More likely, it is the receiver's msgrcv()
call that blocks.
Note well that for a zero-byte file, the very first fread()
call in read_file()
will return 0, and therefore the body of the while
loop will not be executed even once. Only in that loop body is msg.type
ever set, so when read_file()
sends its end-of-file message, the message type is still indeterminate.
But the receiver requests messages of type 1 (only), so it is unreasonable to expect that it will dequeue the reader's end-of-file message in this case (that message's type most likely being different from 1).
Simple solutions:
in read_file()
, lift the assignment msg.type = 1;
out of the loop and before.
OR
in write_file()
, in the msgrcv()
call, specify message type 0, so as to always receive the next message regardless of type.
Or both.