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