/* Destroy the locck handle H and release the lock. */ void dotlock_destroy (dotlock_t h) { dotlock_t hprev, htmp; if ( !h ) return; /* First remove the handle from our global list of all locks. */ LOCK_all_lockfiles (); for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) if (htmp == h) { if (hprev) hprev->next = htmp->next; else all_lockfiles = htmp->next; h->next = NULL; break; } UNLOCK_all_lockfiles (); /* Then destroy the lock. */ if (!h->disable) { #ifdef HAVE_DOSISH_SYSTEM dotlock_destroy_w32 (h); #else /* !HAVE_DOSISH_SYSTEM */ dotlock_destroy_unix (h); #endif /* HAVE_DOSISH_SYSTEM */ jnlib_free (h->lockname); } jnlib_free(h); }
/* Locking core for Windows. This version does not need a temporary file but uses the plain lock file along with record locking. We create this file here so that we later only need to do the file locking. For error reporting it is useful to keep the name of the file in the handle. */ static dotlock_t dotlock_create_w32 (dotlock_t h, const char *file_to_lock) { LOCK_all_lockfiles (); h->next = all_lockfiles; all_lockfiles = h; h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 ); if (!h->lockname) { all_lockfiles = h->next; UNLOCK_all_lockfiles (); jnlib_free (h); return NULL; } strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE along with FILE_SHARE_DELETE but that does not work due to a race condition: Despite the OPEN_ALWAYS flag CreateFile may return an error and we can't reliable create/open the lock file unless we would wait here until it works - however there are other valid reasons why a lock file can't be created and thus the process would not stop as expected but spin until Windows crashes. Our solution is to keep the lock file open; that does not harm. */ { #ifdef HAVE_W32CE_SYSTEM wchar_t *wname = utf8_to_wchar (h->lockname); if (wname) h->lockhd = CreateFile (wname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); else h->lockhd = INVALID_HANDLE_VALUE; jnlib_free (wname); #else h->lockhd = CreateFile (h->lockname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); #endif } if (h->lockhd == INVALID_HANDLE_VALUE) { all_lockfiles = h->next; UNLOCK_all_lockfiles (); my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); jnlib_free (h->lockname); jnlib_free (h); return NULL; } return h; }
static int use_hardlinks_p (const char *tname) { char *lname; struct stat sb; unsigned int nlink; int res; if (stat (tname, &sb)) return -1; nlink = (unsigned int)sb.st_nlink; lname = jnlib_malloc (strlen (tname) + 1 + 1); if (!lname) return -1; strcpy (lname, tname); strcat (lname, "x"); link (tname, lname); if (stat (tname, &sb)) res = -1; /* Ooops. */ else if (sb.st_nlink == nlink + 1) res = 0; /* Yeah, hardlinks are supported. */ else res = 1; /* No hardlink support. */ unlink (lname); jnlib_free (lname); return res; }
/* Unix specific code of destroy_dotlock. */ static void dotlock_destroy_unix (dotlock_t h) { if (h->locked && h->lockname) unlink (h->lockname); if (h->tname && !h->use_o_excl) unlink (h->tname); jnlib_free (h->tname); }
static int fun_closer (void *cookie_arg) { struct fun_cookie_s *cookie = cookie_arg; if (cookie->fd != -1 && cookie->fd != 2) close (cookie->fd); jnlib_free (cookie); log_socket = -1; return 0; }
/* Check whether the files NAME1 and NAME2 are identical. This is for example achieved by comparing the inode numbers of the files. */ int same_file_p (const char *name1, const char *name2) { int yes; /* First try a shortcut. */ if (!compare_filenames (name1, name2)) yes = 1; else { #ifdef HAVE_W32_SYSTEM HANDLE file1, file2; BY_HANDLE_FILE_INFORMATION info1, info2; #ifdef HAVE_W32CE_SYSTEM { wchar_t *wname = utf8_to_wchar (name1); if (wname) file1 = CreateFile (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); else file1 = INVALID_HANDLE_VALUE; jnlib_free (wname); } #else file1 = CreateFile (name1, 0, 0, NULL, OPEN_EXISTING, 0, NULL); #endif if (file1 == INVALID_HANDLE_VALUE) yes = 0; /* If we can't open the file, it is not the same. */ else { #ifdef HAVE_W32CE_SYSTEM { wchar_t *wname = utf8_to_wchar (name2); if (wname) file2 = CreateFile (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); else file2 = INVALID_HANDLE_VALUE; jnlib_free (wname); } #else file2 = CreateFile (name2, 0, 0, NULL, OPEN_EXISTING, 0, NULL); #endif if (file2 == INVALID_HANDLE_VALUE) yes = 0; /* If we can't open the file, it is not the same. */ else { yes = (GetFileInformationByHandle (file1, &info1) && GetFileInformationByHandle (file2, &info2) && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber && info1.nFileIndexHigh == info2.nFileIndexHigh && info1.nFileIndexLow == info2.nFileIndexLow); CloseHandle (file2); } CloseHandle (file1); } #else /*!HAVE_W32_SYSTEM*/ struct stat info1, info2; yes = (!stat (name1, &info1) && !stat (name2, &info2) && info1.st_dev == info2.st_dev && info1.st_ino == info2.st_ino); #endif /*!HAVE_W32_SYSTEM*/ } return yes; }
/* Because we can't use our jnlib_free macro in inline functions we provide this wrapper. */ void _jnlib_free (void *p) { if (p) jnlib_free (p); }
/* Locking core for Unix. It used a temporary file and the link system call to make locking an atomic operation. */ static dotlock_t dotlock_create_unix (dotlock_t h, const char *file_to_lock) { int fd = -1; char pidstr[16]; const char *nodename; const char *dirpart; int dirpartlen; struct utsname utsbuf; size_t tnamelen; snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); /* Create a temporary file. */ if ( uname ( &utsbuf ) ) nodename = "unknown"; else nodename = utsbuf.nodename; if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) ) { dirpart = EXTSEP_S; dirpartlen = 1; } else { dirpartlen = dirpart - file_to_lock; dirpart = file_to_lock; } LOCK_all_lockfiles (); h->next = all_lockfiles; all_lockfiles = h; tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; h->tname = jnlib_malloc (tnamelen + 1); if (!h->tname) { all_lockfiles = h->next; UNLOCK_all_lockfiles (); jnlib_free (h); return NULL; } h->nodename_len = strlen (nodename); snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); h->nodename_off = strlen (h->tname); snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, "%s.%d", nodename, (int)getpid ()); do { jnlib_set_errno (0); fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); } while (fd == -1 && errno == EINTR); if ( fd == -1 ) { all_lockfiles = h->next; UNLOCK_all_lockfiles (); my_error_2 (_("failed to create temporary file `%s': %s\n"), h->tname, strerror(errno)); jnlib_free (h->tname); jnlib_free (h); return NULL; } if ( write (fd, pidstr, 11 ) != 11 ) goto write_failed; if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) goto write_failed; if ( write (fd, "\n", 1 ) != 1 ) goto write_failed; if ( close (fd) ) goto write_failed; /* Check whether we support hard links. */ switch (use_hardlinks_p (h->tname)) { case 0: /* Yes. */ break; case 1: /* No. */ unlink (h->tname); h->use_o_excl = 1; break; default: my_error_2 ("can't check whether hardlinks are supported for `%s': %s\n", h->tname, strerror(errno)); goto write_failed; } h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 ); if (!h->lockname) { all_lockfiles = h->next; UNLOCK_all_lockfiles (); unlink (h->tname); jnlib_free (h->tname); jnlib_free (h); return NULL; } strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); UNLOCK_all_lockfiles (); if (h->use_o_excl) my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname); return h; write_failed: all_lockfiles = h->next; UNLOCK_all_lockfiles (); my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno)); close (fd); unlink (h->tname); jnlib_free (h->tname); jnlib_free (h); return NULL; }
static int read_lockfile (dotlock_t h, int *same_node ) { char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node names are usually shorter. */ int fd; int pid = -1; char *buffer, *p; size_t expected_len; int res, nread; *same_node = 0; expected_len = 10 + 1 + h->nodename_len + 1; if ( expected_len >= sizeof buffer_space) { buffer = jnlib_malloc (expected_len); if (!buffer) return -1; } else buffer = buffer_space; if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) { int e = errno; my_info_2 ("error opening lockfile `%s': %s\n", h->lockname, strerror(errno) ); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (e); /* Need to return ERRNO here. */ return -1; } p = buffer; nread = 0; do { res = read (fd, p, expected_len - nread); if (res == -1 && errno == EINTR) continue; if (res < 0) { my_info_1 ("error reading lockfile `%s'\n", h->lockname ); close (fd); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */ return -1; } p += res; nread += res; } while (res && nread != expected_len); close(fd); if (nread < 11) { my_info_1 ("invalid size of lockfile `%s'\n", h->lockname); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */ return -1; } if (buffer[10] != '\n' || (buffer[10] = 0, pid = atoi (buffer)) == -1 || !pid ) { my_error_2 ("invalid pid %d in lockfile `%s'\n", pid, h->lockname); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (0); return -1; } if (nread == expected_len && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) && buffer[11+h->nodename_len] == '\n') *same_node = 1; if (buffer != buffer_space) jnlib_free (buffer); return pid; }