Blame src/fcatomic.c

Packit 352660
/*
Packit 352660
 * fontconfig/src/fcatomic.c
Packit 352660
 *
Packit 352660
 * Copyright © 2002 Keith Packard
Packit 352660
 *
Packit 352660
 * Permission to use, copy, modify, distribute, and sell this software and its
Packit 352660
 * documentation for any purpose is hereby granted without fee, provided that
Packit 352660
 * the above copyright notice appear in all copies and that both that
Packit 352660
 * copyright notice and this permission notice appear in supporting
Packit 352660
 * documentation, and that the name of the author(s) not be used in
Packit 352660
 * advertising or publicity pertaining to distribution of the software without
Packit 352660
 * specific, written prior permission.  The authors make no
Packit 352660
 * representations about the suitability of this software for any purpose.  It
Packit 352660
 * is provided "as is" without express or implied warranty.
Packit 352660
 *
Packit 352660
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
Packit 352660
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
Packit 352660
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
Packit 352660
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
Packit 352660
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
Packit 352660
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Packit 352660
 * PERFORMANCE OF THIS SOFTWARE.
Packit 352660
 */
Packit 352660
Packit 352660
/*
Packit 352660
 * fcatomic.c
Packit 352660
 *
Packit 352660
 * Lock cache and configuration files for atomic update
Packit 352660
 *
Packit 352660
 * Uses only regular filesystem calls so it should
Packit 352660
 * work even in the absense of functioning file locking
Packit 352660
 *
Packit 352660
 * On Unix, four files are used:
Packit 352660
 *	file	    - the data file accessed by other apps.
Packit 352660
 *	new	    - a new version of the data file while it's being written
Packit 352660
 *	lck	    - the lock file
Packit 352660
 *	tmp	    - a temporary file made unique with mkstemp
Packit 352660
 *
Packit 352660
 *  Here's how it works:
Packit 352660
 *	Create 'tmp' and store our PID in it
Packit 352660
 *	Attempt to link it to 'lck'
Packit 352660
 *	Unlink 'tmp'
Packit 352660
 *	If the link succeeded, the lock is held
Packit 352660
 *
Packit 352660
 * On Windows, where there are no links, no tmp file is used, and lck
Packit 352660
 * is a directory that's mkdir'ed. If the mkdir succeeds, the lock is
Packit 352660
 * held.
Packit 352660
 */
Packit 352660
Packit 352660
#include "fcint.h"
Packit 352660
#include <sys/types.h>
Packit 352660
#include <sys/stat.h>
Packit 352660
#include <unistd.h>
Packit 352660
#include <stdlib.h>
Packit 352660
#include <time.h>
Packit 352660
Packit 352660
#ifdef _WIN32
Packit 352660
#include <direct.h>
Packit 352660
#define mkdir(path,mode) _mkdir(path)
Packit 352660
#endif
Packit 352660
Packit 352660
#define NEW_NAME	".NEW"
Packit 352660
#define LCK_NAME	".LCK"
Packit 352660
#define TMP_NAME	".TMP-XXXXXX"
Packit 352660
Packit 352660
FcAtomic *
Packit 352660
FcAtomicCreate (const FcChar8   *file)
Packit 352660
{
Packit 352660
    int	    file_len = strlen ((char *) file);
Packit 352660
    int	    new_len = file_len + sizeof (NEW_NAME);
Packit 352660
    int	    lck_len = file_len + sizeof (LCK_NAME);
Packit 352660
    int	    tmp_len = file_len + sizeof (TMP_NAME);
Packit 352660
    int	    total_len = (sizeof (FcAtomic) +
Packit 352660
			 file_len + 1 +
Packit 352660
			 new_len + 1 +
Packit 352660
			 lck_len + 1 +
Packit 352660
			 tmp_len + 1);
Packit 352660
    FcAtomic	*atomic = malloc (total_len);
Packit 352660
    if (!atomic)
Packit 352660
	return 0;
Packit 352660
Packit 352660
    atomic->file = (FcChar8 *) (atomic + 1);
Packit 352660
    strcpy ((char *) atomic->file, (char *) file);
Packit 352660
Packit 352660
    atomic->new = atomic->file + file_len + 1;
Packit 352660
    strcpy ((char *) atomic->new, (char *) file);
Packit 352660
    strcat ((char *) atomic->new, NEW_NAME);
Packit 352660
Packit 352660
    atomic->lck = atomic->new + new_len + 1;
Packit 352660
    strcpy ((char *) atomic->lck, (char *) file);
Packit 352660
    strcat ((char *) atomic->lck, LCK_NAME);
Packit 352660
Packit 352660
    atomic->tmp = atomic->lck + lck_len + 1;
Packit 352660
Packit 352660
    return atomic;
Packit 352660
}
Packit 352660
Packit 352660
FcBool
Packit 352660
FcAtomicLock (FcAtomic *atomic)
Packit 352660
{
Packit 352660
    int		ret;
Packit 352660
    struct stat	lck_stat;
Packit 352660
Packit 352660
#ifdef HAVE_LINK
Packit 352660
    int		fd = -1;
Packit 352660
    FILE	*f = 0;
Packit 352660
    FcBool	no_link = FcFalse;
Packit 352660
Packit 352660
    strcpy ((char *) atomic->tmp, (char *) atomic->file);
Packit 352660
    strcat ((char *) atomic->tmp, TMP_NAME);
Packit 352660
    fd = FcMakeTempfile ((char *) atomic->tmp);
Packit 352660
    if (fd < 0)
Packit 352660
	return FcFalse;
Packit 352660
    f = fdopen (fd, "w");
Packit 352660
    if (!f)
Packit 352660
    {
Packit 352660
    	close (fd);
Packit 352660
	unlink ((char *) atomic->tmp);
Packit 352660
	return FcFalse;
Packit 352660
    }
Packit 352660
    ret = fprintf (f, "%ld\n", (long)getpid());
Packit 352660
    if (ret <= 0)
Packit 352660
    {
Packit 352660
	fclose (f);
Packit 352660
	unlink ((char *) atomic->tmp);
Packit 352660
	return FcFalse;
Packit 352660
    }
Packit 352660
    if (fclose (f) == EOF)
Packit 352660
    {
Packit 352660
	unlink ((char *) atomic->tmp);
Packit 352660
	return FcFalse;
Packit 352660
    }
Packit 352660
    ret = link ((char *) atomic->tmp, (char *) atomic->lck);
Packit 352660
    if (ret < 0 && (errno == EPERM || errno == ENOTSUP || errno == EACCES))
Packit 352660
    {
Packit 352660
	/* the filesystem where atomic->lck points to may not supports
Packit 352660
	 * the hard link. so better try to fallback
Packit 352660
	 */
Packit 352660
	ret = mkdir ((char *) atomic->lck, 0600);
Packit 352660
	no_link = FcTrue;
Packit 352660
    }
Packit 352660
    (void) unlink ((char *) atomic->tmp);
Packit 352660
#else
Packit 352660
    ret = mkdir ((char *) atomic->lck, 0600);
Packit 352660
#endif
Packit 352660
    if (ret < 0)
Packit 352660
    {
Packit 352660
	/*
Packit 352660
	 * If the file is around and old (> 10 minutes),
Packit 352660
	 * assume the lock is stale.  This assumes that any
Packit 352660
	 * machines sharing the same filesystem will have clocks
Packit 352660
	 * reasonably close to each other.
Packit 352660
	 */
Packit 352660
	if (FcStat (atomic->lck, &lck_stat) >= 0)
Packit 352660
	{
Packit 352660
	    time_t  now = time (0);
Packit 352660
	    if ((long int) (now - lck_stat.st_mtime) > 10 * 60)
Packit 352660
	    {
Packit 352660
#ifdef HAVE_LINK
Packit 352660
		if (no_link)
Packit 352660
		{
Packit 352660
		    if (rmdir ((char *) atomic->lck) == 0)
Packit 352660
			return FcAtomicLock (atomic);
Packit 352660
		}
Packit 352660
		else
Packit 352660
		{
Packit 352660
		    if (unlink ((char *) atomic->lck) == 0)
Packit 352660
			return FcAtomicLock (atomic);
Packit 352660
		}
Packit 352660
#else
Packit 352660
		if (rmdir ((char *) atomic->lck) == 0)
Packit 352660
		    return FcAtomicLock (atomic);
Packit 352660
#endif
Packit 352660
	    }
Packit 352660
	}
Packit 352660
	return FcFalse;
Packit 352660
    }
Packit 352660
    (void) unlink ((char *) atomic->new);
Packit 352660
    return FcTrue;
Packit 352660
}
Packit 352660
Packit 352660
FcChar8 *
Packit 352660
FcAtomicNewFile (FcAtomic *atomic)
Packit 352660
{
Packit 352660
    return atomic->new;
Packit 352660
}
Packit 352660
Packit 352660
FcChar8 *
Packit 352660
FcAtomicOrigFile (FcAtomic *atomic)
Packit 352660
{
Packit 352660
    return atomic->file;
Packit 352660
}
Packit 352660
Packit 352660
FcBool
Packit 352660
FcAtomicReplaceOrig (FcAtomic *atomic)
Packit 352660
{
Packit 352660
#ifdef _WIN32
Packit 352660
    unlink ((const char *) atomic->file);
Packit 352660
#endif
Packit 352660
    if (rename ((char *) atomic->new, (char *) atomic->file) < 0)
Packit 352660
	return FcFalse;
Packit 352660
    return FcTrue;
Packit 352660
}
Packit 352660
Packit 352660
void
Packit 352660
FcAtomicDeleteNew (FcAtomic *atomic)
Packit 352660
{
Packit 352660
    unlink ((char *) atomic->new);
Packit 352660
}
Packit 352660
Packit 352660
void
Packit 352660
FcAtomicUnlock (FcAtomic *atomic)
Packit 352660
{
Packit 352660
#ifdef HAVE_LINK
Packit 352660
    if (unlink ((char *) atomic->lck) == -1)
Packit 352660
	rmdir ((char *) atomic->lck);
Packit 352660
#else
Packit 352660
    rmdir ((char *) atomic->lck);
Packit 352660
#endif
Packit 352660
}
Packit 352660
Packit 352660
void
Packit 352660
FcAtomicDestroy (FcAtomic *atomic)
Packit 352660
{
Packit 352660
    free (atomic);
Packit 352660
}
Packit 352660
#define __fcatomic__
Packit 352660
#include "fcaliastail.h"
Packit 352660
#undef __fcatomic__