Search code examples
cipcfwritefread

Why is fread being blocked when reading and copying a 0 bytes file?


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


Solution

  • 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.