AFS file locking with Perl

Recent versions of OpenAFS obey advisory locks. This means that lockf() and fcntl() locking will work properly across the network (flock() will never work since it does not support network filesystems). The historical catch with AFS locking is that attempts to lock a locked file will return EAGAIN in every case, not only in the “try lock” case.

You can use the Fcntl module of Perl to accomplish the locking, but File::lockf (available on CPAN) makes it much simpler if all you need is exclusive locking. The main difference is that lockf will not lock a file that is opened readonly since it only supports exclusive locks. If you need specific reader/writer locks you must use Fcntl instead.

Since lockf won’t function on a file opened readonly, we open read-write, and use O_APPEND and omit O_TRUNC to ensure that we do not accidentally overwrite any part of the file through the file handle used for the locking. You must also ensure that the file’s current position points to 0, otherwise the lock will be placed past the end of the file and be nonfunctional.

use Fcntl ':seek';
use File::lockf;
use IO::File;

my $lockfh = IO::File->new;
#Alternate method:
#sysopen($lockfh, "$fname", O_WRONLY|O_APPEND|O_CREAT) or die "Couldn't open $fname: $!";
$lockfh->open(">>$fname") or die "Couldn't open $fname: $!";
seek $lockfh,0,SEEK_SET or die "Couldn't seek: $!";

my $lock = new File::lockf(\$lockfh);
my $locked = 0;

while (1) {
        my $now_string = localtime;
        if ($lock->tlock == 0) {
                if ($locked) {
                        warn "$now_string: lock on $fname acquired\n";
        else {
                warn "$now_string: can't immediately write-lock $fname ($!), trying again in 60 seconds...\n";
        if ($locked > 10) {
                die "$now_string: giving up after $locked tries to lock $fname\n";

my $fh = IO::File->new;
$fh->open(">$fname") or die "Couldn't open $fname: $!";

... rest of program here ...

close $fh;
if ($lock->ulock != 0) { warn "Failed to release lock on $fname" };
close $lockfh;
exit 0;

Leave a Reply