fcatomic.c revision ca08ab68
12c393a42Smrg/* 2a6844aabSmrg * fontconfig/src/fcatomic.c 32c393a42Smrg * 42c393a42Smrg * Copyright © 2002 Keith Packard 52c393a42Smrg * 62c393a42Smrg * Permission to use, copy, modify, distribute, and sell this software and its 72c393a42Smrg * documentation for any purpose is hereby granted without fee, provided that 82c393a42Smrg * the above copyright notice appear in all copies and that both that 92c393a42Smrg * copyright notice and this permission notice appear in supporting 10ca08ab68Smrg * documentation, and that the name of the author(s) not be used in 112c393a42Smrg * advertising or publicity pertaining to distribution of the software without 12ca08ab68Smrg * specific, written prior permission. The authors make no 132c393a42Smrg * representations about the suitability of this software for any purpose. It 142c393a42Smrg * is provided "as is" without express or implied warranty. 152c393a42Smrg * 16a6844aabSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18a6844aabSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 192c393a42Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 202c393a42Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 212c393a42Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 222c393a42Smrg * PERFORMANCE OF THIS SOFTWARE. 232c393a42Smrg */ 242c393a42Smrg 252c393a42Smrg/* 262c393a42Smrg * fcatomic.c 272c393a42Smrg * 282c393a42Smrg * Lock cache and configuration files for atomic update 292c393a42Smrg * 302c393a42Smrg * Uses only regular filesystem calls so it should 312c393a42Smrg * work even in the absense of functioning file locking 322c393a42Smrg * 332c393a42Smrg * On Unix, four files are used: 342c393a42Smrg * file - the data file accessed by other apps. 352c393a42Smrg * new - a new version of the data file while it's being written 362c393a42Smrg * lck - the lock file 372c393a42Smrg * tmp - a temporary file made unique with mkstemp 382c393a42Smrg * 392c393a42Smrg * Here's how it works: 402c393a42Smrg * Create 'tmp' and store our PID in it 412c393a42Smrg * Attempt to link it to 'lck' 422c393a42Smrg * Unlink 'tmp' 432c393a42Smrg * If the link succeeded, the lock is held 442c393a42Smrg * 452c393a42Smrg * On Windows, where there are no links, no tmp file is used, and lck 462c393a42Smrg * is a directory that's mkdir'ed. If the mkdir succeeds, the lock is 472c393a42Smrg * held. 482c393a42Smrg */ 492c393a42Smrg 502c393a42Smrg#include "fcint.h" 512c393a42Smrg#include <sys/types.h> 522c393a42Smrg#include <sys/stat.h> 532c393a42Smrg#include <fcntl.h> 542c393a42Smrg#include <unistd.h> 552c393a42Smrg#include <stdlib.h> 562c393a42Smrg#include <time.h> 572c393a42Smrg 582c393a42Smrg#ifdef _WIN32 592c393a42Smrg#define mkdir(path,mode) _mkdir(path) 602c393a42Smrg#endif 612c393a42Smrg 622c393a42Smrg#define NEW_NAME ".NEW" 632c393a42Smrg#define LCK_NAME ".LCK" 642c393a42Smrg#define TMP_NAME ".TMP-XXXXXX" 652c393a42Smrg 662c393a42SmrgFcAtomic * 672c393a42SmrgFcAtomicCreate (const FcChar8 *file) 682c393a42Smrg{ 692c393a42Smrg int file_len = strlen ((char *) file); 702c393a42Smrg int new_len = file_len + sizeof (NEW_NAME); 712c393a42Smrg int lck_len = file_len + sizeof (LCK_NAME); 722c393a42Smrg int tmp_len = file_len + sizeof (TMP_NAME); 732c393a42Smrg int total_len = (sizeof (FcAtomic) + 742c393a42Smrg file_len + 1 + 752c393a42Smrg new_len + 1 + 762c393a42Smrg lck_len + 1 + 772c393a42Smrg tmp_len + 1); 782c393a42Smrg FcAtomic *atomic = malloc (total_len); 792c393a42Smrg if (!atomic) 802c393a42Smrg return 0; 812c393a42Smrg FcMemAlloc (FC_MEM_ATOMIC, total_len); 82ca08ab68Smrg 832c393a42Smrg atomic->file = (FcChar8 *) (atomic + 1); 842c393a42Smrg strcpy ((char *) atomic->file, (char *) file); 852c393a42Smrg 862c393a42Smrg atomic->new = atomic->file + file_len + 1; 872c393a42Smrg strcpy ((char *) atomic->new, (char *) file); 882c393a42Smrg strcat ((char *) atomic->new, NEW_NAME); 892c393a42Smrg 902c393a42Smrg atomic->lck = atomic->new + new_len + 1; 912c393a42Smrg strcpy ((char *) atomic->lck, (char *) file); 922c393a42Smrg strcat ((char *) atomic->lck, LCK_NAME); 932c393a42Smrg 942c393a42Smrg atomic->tmp = atomic->lck + lck_len + 1; 952c393a42Smrg 962c393a42Smrg return atomic; 972c393a42Smrg} 982c393a42Smrg 992c393a42SmrgFcBool 1002c393a42SmrgFcAtomicLock (FcAtomic *atomic) 1012c393a42Smrg{ 1022c393a42Smrg int ret; 1032c393a42Smrg struct stat lck_stat; 1042c393a42Smrg 1052c393a42Smrg#ifdef HAVE_LINK 106ca08ab68Smrg int fd = -1; 107ca08ab68Smrg FILE *f = 0; 108ca08ab68Smrg FcBool no_link = FcFalse; 109ca08ab68Smrg 1102c393a42Smrg strcpy ((char *) atomic->tmp, (char *) atomic->file); 1112c393a42Smrg strcat ((char *) atomic->tmp, TMP_NAME); 1122c393a42Smrg fd = mkstemp ((char *) atomic->tmp); 1132c393a42Smrg if (fd < 0) 1142c393a42Smrg return FcFalse; 1152c393a42Smrg f = fdopen (fd, "w"); 1162c393a42Smrg if (!f) 1172c393a42Smrg { 1182c393a42Smrg close (fd); 1192c393a42Smrg unlink ((char *) atomic->tmp); 1202c393a42Smrg return FcFalse; 1212c393a42Smrg } 1222c393a42Smrg ret = fprintf (f, "%ld\n", (long)getpid()); 1232c393a42Smrg if (ret <= 0) 1242c393a42Smrg { 1252c393a42Smrg fclose (f); 1262c393a42Smrg unlink ((char *) atomic->tmp); 1272c393a42Smrg return FcFalse; 1282c393a42Smrg } 1292c393a42Smrg if (fclose (f) == EOF) 1302c393a42Smrg { 1312c393a42Smrg unlink ((char *) atomic->tmp); 1322c393a42Smrg return FcFalse; 1332c393a42Smrg } 1342c393a42Smrg ret = link ((char *) atomic->tmp, (char *) atomic->lck); 135ca08ab68Smrg if (ret < 0 && errno == EPERM) 136ca08ab68Smrg { 137ca08ab68Smrg /* the filesystem where atomic->lck points to may not supports 138ca08ab68Smrg * the hard link. so better try to fallback 139ca08ab68Smrg */ 140ca08ab68Smrg ret = mkdir ((char *) atomic->lck, 0600); 141ca08ab68Smrg no_link = FcTrue; 142ca08ab68Smrg } 1432c393a42Smrg (void) unlink ((char *) atomic->tmp); 1442c393a42Smrg#else 1452c393a42Smrg ret = mkdir ((char *) atomic->lck, 0600); 1462c393a42Smrg#endif 1472c393a42Smrg if (ret < 0) 1482c393a42Smrg { 1492c393a42Smrg /* 1502c393a42Smrg * If the file is around and old (> 10 minutes), 1512c393a42Smrg * assume the lock is stale. This assumes that any 1522c393a42Smrg * machines sharing the same filesystem will have clocks 1532c393a42Smrg * reasonably close to each other. 1542c393a42Smrg */ 155ca08ab68Smrg if (FcStat (atomic->lck, &lck_stat) >= 0) 1562c393a42Smrg { 1572c393a42Smrg time_t now = time (0); 1582c393a42Smrg if ((long int) (now - lck_stat.st_mtime) > 10 * 60) 1592c393a42Smrg { 1602c393a42Smrg#ifdef HAVE_LINK 161ca08ab68Smrg if (no_link) 162ca08ab68Smrg { 163ca08ab68Smrg if (rmdir ((char *) atomic->lck) == 0) 164ca08ab68Smrg return FcAtomicLock (atomic); 165ca08ab68Smrg } 166ca08ab68Smrg else 167ca08ab68Smrg { 168ca08ab68Smrg if (unlink ((char *) atomic->lck) == 0) 169ca08ab68Smrg return FcAtomicLock (atomic); 170ca08ab68Smrg } 1712c393a42Smrg#else 1722c393a42Smrg if (rmdir ((char *) atomic->lck) == 0) 1732c393a42Smrg return FcAtomicLock (atomic); 1742c393a42Smrg#endif 1752c393a42Smrg } 1762c393a42Smrg } 1772c393a42Smrg return FcFalse; 1782c393a42Smrg } 1792c393a42Smrg (void) unlink ((char *) atomic->new); 1802c393a42Smrg return FcTrue; 1812c393a42Smrg} 1822c393a42Smrg 1832c393a42SmrgFcChar8 * 1842c393a42SmrgFcAtomicNewFile (FcAtomic *atomic) 1852c393a42Smrg{ 1862c393a42Smrg return atomic->new; 1872c393a42Smrg} 1882c393a42Smrg 1892c393a42SmrgFcChar8 * 1902c393a42SmrgFcAtomicOrigFile (FcAtomic *atomic) 1912c393a42Smrg{ 1922c393a42Smrg return atomic->file; 1932c393a42Smrg} 1942c393a42Smrg 1952c393a42SmrgFcBool 1962c393a42SmrgFcAtomicReplaceOrig (FcAtomic *atomic) 1972c393a42Smrg{ 1982c393a42Smrg#ifdef _WIN32 199ca08ab68Smrg unlink ((const char *) atomic->file); 2002c393a42Smrg#endif 2012c393a42Smrg if (rename ((char *) atomic->new, (char *) atomic->file) < 0) 2022c393a42Smrg return FcFalse; 2032c393a42Smrg return FcTrue; 2042c393a42Smrg} 2052c393a42Smrg 2062c393a42Smrgvoid 2072c393a42SmrgFcAtomicDeleteNew (FcAtomic *atomic) 2082c393a42Smrg{ 2092c393a42Smrg unlink ((char *) atomic->new); 2102c393a42Smrg} 2112c393a42Smrg 2122c393a42Smrgvoid 2132c393a42SmrgFcAtomicUnlock (FcAtomic *atomic) 2142c393a42Smrg{ 2152c393a42Smrg#ifdef HAVE_LINK 216ca08ab68Smrg if (unlink ((char *) atomic->lck) == -1) 217ca08ab68Smrg rmdir ((char *) atomic->lck); 2182c393a42Smrg#else 2192c393a42Smrg rmdir ((char *) atomic->lck); 2202c393a42Smrg#endif 2212c393a42Smrg} 2222c393a42Smrg 2232c393a42Smrgvoid 2242c393a42SmrgFcAtomicDestroy (FcAtomic *atomic) 2252c393a42Smrg{ 2262c393a42Smrg FcMemFree (FC_MEM_ATOMIC, sizeof (FcAtomic) + 2272c393a42Smrg strlen ((char *) atomic->file) * 4 + 4 + 228ca08ab68Smrg sizeof (NEW_NAME) + sizeof (LCK_NAME) + 2292c393a42Smrg sizeof (TMP_NAME)); 2302c393a42Smrg 2312c393a42Smrg free (atomic); 2322c393a42Smrg} 2332c393a42Smrg#define __fcatomic__ 2342c393a42Smrg#include "fcaliastail.h" 2352c393a42Smrg#undef __fcatomic__ 236