Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT sources DOT redhat DOT com Delivered-To: mailing list cygwin AT sources DOT redhat DOT com To: cygwin AT cygwin DOT com Cc: james AT free-lunch DOT demon DOT co DOT uk Subject: link(2) and atomic file creation on NFS From: James Youngman Date: 29 Aug 2001 21:34:42 +0100 Message-ID: Lines: 135 User-Agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii 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 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/