Mail Archives: cygwin/2001/08/29/16:35:20
I have some existing code which uses link(2) to achieve atomic locking
on Unix over NFS (since O_EXCL does not work over NFS). The
application (a GPLed SCCS clone) now compiles under Cygwin, but one
remaining problem is that the lockfile code uses link(2) to create a
hard link to an open file.
This seems not to work under Cygwin B20.1 (over NT4). The link count
of the file stays at 1 (I think that errno=EPERM, but I could be
wrong).
The link(2) usage is only relevant over NFS, but even if there is an
NFS client for NT [*does* one exist?], I would assume that the Cygwin
link(2) implementation would not correspond with a link(2) operation
via the NT NFS client. So, the link(2) implementation loses under
NT. Fine.
Is there a way of safely creating a lock file without race conditions
that will also work under Cygwin? For the sake of the discussion,
here is the current code :-
(explanation: "mystring" is a C++ class a lot like the standard string
class, and in fact usually *is* the standard string class)
TIA for any helpful hints!
/* WARNING: If you use fstat() on the fd to get the link count, the
* wrong value is returned for Linux 2.0.34 and glibc-2.0.7, the
* second time we perform the fstat(). Therefore we use stat(2)
* rather than fstat(2).
*/
static long get_nlinks(const char *name)
{
struct stat st;
if (0 == stat(name, &st))
{
return (long)st.st_nlink;
}
else
{
return -1L;
}
}
static int atomic_nfs_create(const mystring& path, int flags, int perms)
{
mystring dirname, basename;
char buf[32];
const char *pstr = path.c_str();
split_filename(path, dirname, basename);
/* Rely (slightly) on only 11 characters of filename. */
for (long attempt=0; attempt < 10000; ++attempt)
{
/* form the name of a lock file. */
sprintf(buf, "nfslck%ld", attempt);
const mystring lockname = dirname + mystring(buf);
const char *lockstr = lockname.c_str();
errno = 0;
int fd = open(lockstr, flags, perms);
if (fd >= 0)
{
if (1 == get_nlinks(lockstr))
{
int link_errno = 0;
errno = 0;
if (-1 == link(lockstr, pstr))
link_errno = errno;
/* ignore other responses */
if (2 == get_nlinks(lockstr))
{
unlink(lockstr);
return fd; /* success! */
}
else /* link(2) failed. */
{
if (EPERM == link_errno)
{
/* containing filesystem does not support hard links. */
close(fd);
unlink(lockstr);
/* assume that the filesystem supports O_EXCL if it does
* not supprort link(2).
*/
return open(pstr, flags, perms);
}
}
}
close(fd);
unlink(lockstr);
}
else /* open() failed. */
{
switch (errno)
{
case EEXIST:
/* someone else got that lock first; they may in fact not
* be trying to lock the same s-file (but instead another
* s-file in the same directory)
*
* Try again. Sleep first if we're not doing well,
* but try to avoid pathalogical cases...
*/
if ( (attempt > 4) && (attempt & 1) == (getpid() & 1) )
{
errormsg("Sleeping for one second while "
"waiting for lock\n");
sleep(1);
}
break;
default: /* hard failure. */
/* fall back on the less-safe method, which will
* probably still fail
*/
return open(pstr, flags, perms);
}
}
}
return -1;
}
--
James Youngman
Manchester, UK. +44 161 226 7339
PGP (GPG) key ID for <jay AT gnu DOT org> is 64A95EE5 (F1B83152).
--
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
Bug reporting: http://cygwin.com/bugs.html
Documentation: http://cygwin.com/docs.html
FAQ: http://cygwin.com/faq/
- Raw text -