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
50a4e54154Smrg#ifdef HAVE_CONFIG_H
51a4e54154Smrg#include <config.h>
52a4e54154Smrg#endif
53a4e54154Smrg
542c393a42Smrg#include "fcint.h"
552c393a42Smrg#include <sys/types.h>
562c393a42Smrg#include <sys/stat.h>
572c393a42Smrg#include <stdlib.h>
582c393a42Smrg#include <time.h>
592c393a42Smrg
60a4e54154Smrg#ifdef HAVE_UNISTD_H
61a4e54154Smrg#include <unistd.h>
62a4e54154Smrg#endif
63a4e54154Smrg
642c393a42Smrg#ifdef _WIN32
65c9710b42Smrg#include <direct.h>
662c393a42Smrg#define mkdir(path,mode) _mkdir(path)
672c393a42Smrg#endif
682c393a42Smrg
692c393a42Smrg#define NEW_NAME	".NEW"
702c393a42Smrg#define LCK_NAME	".LCK"
712c393a42Smrg#define TMP_NAME	".TMP-XXXXXX"
722c393a42Smrg
732c393a42SmrgFcAtomic *
742c393a42SmrgFcAtomicCreate (const FcChar8   *file)
752c393a42Smrg{
762c393a42Smrg    int	    file_len = strlen ((char *) file);
772c393a42Smrg    int	    new_len = file_len + sizeof (NEW_NAME);
782c393a42Smrg    int	    lck_len = file_len + sizeof (LCK_NAME);
792c393a42Smrg    int	    tmp_len = file_len + sizeof (TMP_NAME);
802c393a42Smrg    int	    total_len = (sizeof (FcAtomic) +
812c393a42Smrg			 file_len + 1 +
822c393a42Smrg			 new_len + 1 +
832c393a42Smrg			 lck_len + 1 +
842c393a42Smrg			 tmp_len + 1);
852c393a42Smrg    FcAtomic	*atomic = malloc (total_len);
862c393a42Smrg    if (!atomic)
872c393a42Smrg	return 0;
88ca08ab68Smrg
892c393a42Smrg    atomic->file = (FcChar8 *) (atomic + 1);
902c393a42Smrg    strcpy ((char *) atomic->file, (char *) file);
912c393a42Smrg
922c393a42Smrg    atomic->new = atomic->file + file_len + 1;
932c393a42Smrg    strcpy ((char *) atomic->new, (char *) file);
942c393a42Smrg    strcat ((char *) atomic->new, NEW_NAME);
952c393a42Smrg
962c393a42Smrg    atomic->lck = atomic->new + new_len + 1;
972c393a42Smrg    strcpy ((char *) atomic->lck, (char *) file);
982c393a42Smrg    strcat ((char *) atomic->lck, LCK_NAME);
992c393a42Smrg
1002c393a42Smrg    atomic->tmp = atomic->lck + lck_len + 1;
1012c393a42Smrg
1022c393a42Smrg    return atomic;
1032c393a42Smrg}
1042c393a42Smrg
1052c393a42SmrgFcBool
1062c393a42SmrgFcAtomicLock (FcAtomic *atomic)
1072c393a42Smrg{
1082c393a42Smrg    int		ret;
1092c393a42Smrg    struct stat	lck_stat;
1102c393a42Smrg
1112c393a42Smrg#ifdef HAVE_LINK
112ca08ab68Smrg    int		fd = -1;
113ca08ab68Smrg    FILE	*f = 0;
114ca08ab68Smrg    FcBool	no_link = FcFalse;
115ca08ab68Smrg
1162c393a42Smrg    strcpy ((char *) atomic->tmp, (char *) atomic->file);
1172c393a42Smrg    strcat ((char *) atomic->tmp, TMP_NAME);
118c9710b42Smrg    fd = FcMakeTempfile ((char *) atomic->tmp);
1192c393a42Smrg    if (fd < 0)
1202c393a42Smrg	return FcFalse;
1212c393a42Smrg    f = fdopen (fd, "w");
1222c393a42Smrg    if (!f)
1232c393a42Smrg    {
1242c393a42Smrg    	close (fd);
1252c393a42Smrg	unlink ((char *) atomic->tmp);
1262c393a42Smrg	return FcFalse;
1272c393a42Smrg    }
1282c393a42Smrg    ret = fprintf (f, "%ld\n", (long)getpid());
1292c393a42Smrg    if (ret <= 0)
1302c393a42Smrg    {
1312c393a42Smrg	fclose (f);
1322c393a42Smrg	unlink ((char *) atomic->tmp);
1332c393a42Smrg	return FcFalse;
1342c393a42Smrg    }
1352c393a42Smrg    if (fclose (f) == EOF)
1362c393a42Smrg    {
1372c393a42Smrg	unlink ((char *) atomic->tmp);
1382c393a42Smrg	return FcFalse;
1392c393a42Smrg    }
1402c393a42Smrg    ret = link ((char *) atomic->tmp, (char *) atomic->lck);
141953daebaSmrg    if (ret < 0 && (errno == EPERM || errno == ENOTSUP || errno == EACCES))
142ca08ab68Smrg    {
143ca08ab68Smrg	/* the filesystem where atomic->lck points to may not supports
144ca08ab68Smrg	 * the hard link. so better try to fallback
145ca08ab68Smrg	 */
146ca08ab68Smrg	ret = mkdir ((char *) atomic->lck, 0600);
147ca08ab68Smrg	no_link = FcTrue;
148ca08ab68Smrg    }
1492c393a42Smrg    (void) unlink ((char *) atomic->tmp);
1502c393a42Smrg#else
1512c393a42Smrg    ret = mkdir ((char *) atomic->lck, 0600);
1522c393a42Smrg#endif
1532c393a42Smrg    if (ret < 0)
1542c393a42Smrg    {
1552c393a42Smrg	/*
1562c393a42Smrg	 * If the file is around and old (> 10 minutes),
1572c393a42Smrg	 * assume the lock is stale.  This assumes that any
1582c393a42Smrg	 * machines sharing the same filesystem will have clocks
1592c393a42Smrg	 * reasonably close to each other.
1602c393a42Smrg	 */
161ca08ab68Smrg	if (FcStat (atomic->lck, &lck_stat) >= 0)
1622c393a42Smrg	{
1632c393a42Smrg	    time_t  now = time (0);
1642c393a42Smrg	    if ((long int) (now - lck_stat.st_mtime) > 10 * 60)
1652c393a42Smrg	    {
1662c393a42Smrg#ifdef HAVE_LINK
167ca08ab68Smrg		if (no_link)
168ca08ab68Smrg		{
169ca08ab68Smrg		    if (rmdir ((char *) atomic->lck) == 0)
170ca08ab68Smrg			return FcAtomicLock (atomic);
171ca08ab68Smrg		}
172ca08ab68Smrg		else
173ca08ab68Smrg		{
174ca08ab68Smrg		    if (unlink ((char *) atomic->lck) == 0)
175ca08ab68Smrg			return FcAtomicLock (atomic);
176ca08ab68Smrg		}
1772c393a42Smrg#else
1782c393a42Smrg		if (rmdir ((char *) atomic->lck) == 0)
1792c393a42Smrg		    return FcAtomicLock (atomic);
1802c393a42Smrg#endif
1812c393a42Smrg	    }
1822c393a42Smrg	}
1832c393a42Smrg	return FcFalse;
1842c393a42Smrg    }
1852c393a42Smrg    (void) unlink ((char *) atomic->new);
1862c393a42Smrg    return FcTrue;
1872c393a42Smrg}
1882c393a42Smrg
1892c393a42SmrgFcChar8 *
1902c393a42SmrgFcAtomicNewFile (FcAtomic *atomic)
1912c393a42Smrg{
1922c393a42Smrg    return atomic->new;
1932c393a42Smrg}
1942c393a42Smrg
1952c393a42SmrgFcChar8 *
1962c393a42SmrgFcAtomicOrigFile (FcAtomic *atomic)
1972c393a42Smrg{
1982c393a42Smrg    return atomic->file;
1992c393a42Smrg}
2002c393a42Smrg
2012c393a42SmrgFcBool
2022c393a42SmrgFcAtomicReplaceOrig (FcAtomic *atomic)
2032c393a42Smrg{
2042c393a42Smrg#ifdef _WIN32
205ca08ab68Smrg    unlink ((const char *) atomic->file);
2062c393a42Smrg#endif
2072c393a42Smrg    if (rename ((char *) atomic->new, (char *) atomic->file) < 0)
2082c393a42Smrg	return FcFalse;
2092c393a42Smrg    return FcTrue;
2102c393a42Smrg}
2112c393a42Smrg
2122c393a42Smrgvoid
2132c393a42SmrgFcAtomicDeleteNew (FcAtomic *atomic)
2142c393a42Smrg{
2152c393a42Smrg    unlink ((char *) atomic->new);
2162c393a42Smrg}
2172c393a42Smrg
2182c393a42Smrgvoid
2192c393a42SmrgFcAtomicUnlock (FcAtomic *atomic)
2202c393a42Smrg{
2212c393a42Smrg#ifdef HAVE_LINK
222ca08ab68Smrg    if (unlink ((char *) atomic->lck) == -1)
223ca08ab68Smrg	rmdir ((char *) atomic->lck);
2242c393a42Smrg#else
2252c393a42Smrg    rmdir ((char *) atomic->lck);
2262c393a42Smrg#endif
2272c393a42Smrg}
2282c393a42Smrg
2292c393a42Smrgvoid
2302c393a42SmrgFcAtomicDestroy (FcAtomic *atomic)
2312c393a42Smrg{
2321cc69409Smrg    if (atomic)
2331cc69409Smrg	free (atomic);
2342c393a42Smrg}
2352c393a42Smrg#define __fcatomic__
2362c393a42Smrg#include "fcaliastail.h"
2372c393a42Smrg#undef __fcatomic__
238