[Cascavel-pm] como garantir file lock para outros programas?

Breno G. de Oliveira breno em clavis.com.br
Quarta Janeiro 25 03:08:06 PST 2006


Alceu R. de Freitas Jr. wrote:
> 
> Eu testei esse script contra programas como useradd e
> smbpasswd (do Samba). Para minha surpresa ambos
> puderam alterar os arquivos sem maiores problemas.
> 
> Eu chequei a documentação e o Perl pode usar as
> funções de C flock, lockf e fcntl para fazer o file
> locking, usando a primeira que ele encontrar.
> 
> Eu não chequei o código C desses programas que eu
> testei mas eu duvido que eles não usem file locking
> para evitar problemas com corrupção do arquivo.
> 
> Alguém já lidou com esse tipo de problema? É possível
> ter alguma segurança nesse sentido com programas
> feitos em outras linguagens, mesmo o file locking do
> Perl sendo consultivo?
> 

Oi Alceu,

eu também procuro a algum tempo uma boa maneira de implementar file
locking com Perl. Respondendo a sua pergunta sobre ter "alguma
segurança" com programas feitos em outras linguagens, vale lembrar que o
locking é voluntário, ou seja, se o outro programa não implementar
locking ele vai conseguir escrever no arquivo numa boa (como vc mesmo
lembrou).

Também duvido que o samba não implemente locking, mas pra garantir
testei seu programa contra esse codigo em C que fiz implementando
locking via fcntl():

------------8<-------------
/* Obs: alguns #includes e verificacoes de erros
   foram omitidos para facilitar legibilidade do codigo */

#include <fcntl.h>

int main(void)
{
    int fd;
    struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0 };
    fl.l_pid = getpid();

    fd = open("a.txt", O_WRONLY);
    fcntl(fd, F_SETLKW, &fl);

    printf("ganhei o lock!!!\n");
    sleep(30);
    close(fd);
}
------------8<-------------

O programa funciona exatamente como o seu código Perl: abre um arquivo
para escrita (no caso, "a.txt") e dorme por 30 segundos.

Embora o programa em Perl funcione contra si mesmo e o em C idem, eles
não funcionam entre si. Ou seja, se eu travo o arquivo com o código
Perl, o código C acha que não há locking algum, e vice-versa.

Após certa pesquisa, achei a resposta para tal comportamento na
documentação do kernel Linux:

"The solution I have chosen, after much experimentation and discussion,
is to make flock() and fcntl() locks oblivious to each other. Both can
exist, and neither will have any effect on the other."

o Linus continua a explicação assim:

"I wanted the two lock styles to be cooperative, but there were so many
race and deadlock conditions that the current solution was the only
practical one. It puts us in the same position as, for example, SunOS
4.1.x and several other commercial Unices. The only OS's that support
cooperative flock()/fcntl() are those that emulate flock() using
fcntl(), with all the problems that implies."

Ou seja, se o programa em C faz seu locking via fcntl(), o feito em Perl
só enxerga essa trava se tiver usado a mesma função - o que não foi o
caso, já que usamos a função flock() - e vice-versa.

Para confirmar, troquei a chamada do meu programa em C para usar flock()
ao invés de fcntl() e ele ficou assim:

------------8<-------------
/* Obs: alguns #includes e verificacoes de erros
   foram omitidos para facilitar legibilidade do codigo */

#include <sys/file.h>

int main(void)
{
    int fd;

    fd = open("a.txt", O_WRONLY);
    flock(fd, LOCK_EX);

    printf("ganhei o lock!!!\n");
    sleep(30);
    close(fd);
}
------------8<-------------

O teste foi um sucesso, e o seu programa Perl agora encontra o lock
feito em C e vice-versa!

Infelizmente, como pôde ser constatado, testar locking de programas que
não os seus é inviável (senão impossível) sem que saibamos exatamente
qual o tipo de locking empregado (como foi o seu caso no samba), ou sem
um sistema operacional que faça locking mandatório (nesse caso, qualquer
locking bloquearia chamadas de sistema para escrita - e isso não costuma
dar muito certo).

Uma solução seria usar o flock() e o fcntl() logo depois no mesmo
arquivo, mas isso gera deadlock em sistemas com flock emulado ou que
usem a mesma syscall para ambas as funções. Para contornar esse
problema, se déssemos o flock() mas o fcntl() bloqueasse, pediríamos o
PID do processo "dono" do lock e, caso seja o próprio programa, esse
poderia continuar normalmente.

Infelizmente não pude continuar esse teste pois não me entendi com a
sintaxe da função fcntl() no Perl...

Espero ter ajudado!

[]s

breno



Mais detalhes sobre a lista de discussão Cascavel-pm