fcatomic.c revision a4e54154
1/*
2 * fontconfig/src/fcatomic.c
3 *
4 * Copyright © 2002 Keith Packard
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make no
13 * representations about the suitability of this software for any purpose.  It
14 * is provided "as is" without express or implied warranty.
15 *
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25/*
26 * fcatomic.c
27 *
28 * Lock cache and configuration files for atomic update
29 *
30 * Uses only regular filesystem calls so it should
31 * work even in the absense of functioning file locking
32 *
33 * On Unix, four files are used:
34 *	file	    - the data file accessed by other apps.
35 *	new	    - a new version of the data file while it's being written
36 *	lck	    - the lock file
37 *	tmp	    - a temporary file made unique with mkstemp
38 *
39 *  Here's how it works:
40 *	Create 'tmp' and store our PID in it
41 *	Attempt to link it to 'lck'
42 *	Unlink 'tmp'
43 *	If the link succeeded, the lock is held
44 *
45 * On Windows, where there are no links, no tmp file is used, and lck
46 * is a directory that's mkdir'ed. If the mkdir succeeds, the lock is
47 * held.
48 */
49
50#ifdef HAVE_CONFIG_H
51#include <config.h>
52#endif
53
54#include "fcint.h"
55#include <sys/types.h>
56#include <sys/stat.h>
57#include <stdlib.h>
58#include <time.h>
59
60#ifdef HAVE_UNISTD_H
61#include <unistd.h>
62#endif
63
64#ifdef _WIN32
65#include <direct.h>
66#define mkdir(path,mode) _mkdir(path)
67#endif
68
69#define NEW_NAME	".NEW"
70#define LCK_NAME	".LCK"
71#define TMP_NAME	".TMP-XXXXXX"
72
73FcAtomic *
74FcAtomicCreate (const FcChar8   *file)
75{
76    int	    file_len = strlen ((char *) file);
77    int	    new_len = file_len + sizeof (NEW_NAME);
78    int	    lck_len = file_len + sizeof (LCK_NAME);
79    int	    tmp_len = file_len + sizeof (TMP_NAME);
80    int	    total_len = (sizeof (FcAtomic) +
81			 file_len + 1 +
82			 new_len + 1 +
83			 lck_len + 1 +
84			 tmp_len + 1);
85    FcAtomic	*atomic = malloc (total_len);
86    if (!atomic)
87	return 0;
88
89    atomic->file = (FcChar8 *) (atomic + 1);
90    strcpy ((char *) atomic->file, (char *) file);
91
92    atomic->new = atomic->file + file_len + 1;
93    strcpy ((char *) atomic->new, (char *) file);
94    strcat ((char *) atomic->new, NEW_NAME);
95
96    atomic->lck = atomic->new + new_len + 1;
97    strcpy ((char *) atomic->lck, (char *) file);
98    strcat ((char *) atomic->lck, LCK_NAME);
99
100    atomic->tmp = atomic->lck + lck_len + 1;
101
102    return atomic;
103}
104
105FcBool
106FcAtomicLock (FcAtomic *atomic)
107{
108    int		ret;
109    struct stat	lck_stat;
110
111#ifdef HAVE_LINK
112    int		fd = -1;
113    FILE	*f = 0;
114    FcBool	no_link = FcFalse;
115
116    strcpy ((char *) atomic->tmp, (char *) atomic->file);
117    strcat ((char *) atomic->tmp, TMP_NAME);
118    fd = FcMakeTempfile ((char *) atomic->tmp);
119    if (fd < 0)
120	return FcFalse;
121    f = fdopen (fd, "w");
122    if (!f)
123    {
124    	close (fd);
125	unlink ((char *) atomic->tmp);
126	return FcFalse;
127    }
128    ret = fprintf (f, "%ld\n", (long)getpid());
129    if (ret <= 0)
130    {
131	fclose (f);
132	unlink ((char *) atomic->tmp);
133	return FcFalse;
134    }
135    if (fclose (f) == EOF)
136    {
137	unlink ((char *) atomic->tmp);
138	return FcFalse;
139    }
140    ret = link ((char *) atomic->tmp, (char *) atomic->lck);
141    if (ret < 0 && (errno == EPERM || errno == ENOTSUP || errno == EACCES))
142    {
143	/* the filesystem where atomic->lck points to may not supports
144	 * the hard link. so better try to fallback
145	 */
146	ret = mkdir ((char *) atomic->lck, 0600);
147	no_link = FcTrue;
148    }
149    (void) unlink ((char *) atomic->tmp);
150#else
151    ret = mkdir ((char *) atomic->lck, 0600);
152#endif
153    if (ret < 0)
154    {
155	/*
156	 * If the file is around and old (> 10 minutes),
157	 * assume the lock is stale.  This assumes that any
158	 * machines sharing the same filesystem will have clocks
159	 * reasonably close to each other.
160	 */
161	if (FcStat (atomic->lck, &lck_stat) >= 0)
162	{
163	    time_t  now = time (0);
164	    if ((long int) (now - lck_stat.st_mtime) > 10 * 60)
165	    {
166#ifdef HAVE_LINK
167		if (no_link)
168		{
169		    if (rmdir ((char *) atomic->lck) == 0)
170			return FcAtomicLock (atomic);
171		}
172		else
173		{
174		    if (unlink ((char *) atomic->lck) == 0)
175			return FcAtomicLock (atomic);
176		}
177#else
178		if (rmdir ((char *) atomic->lck) == 0)
179		    return FcAtomicLock (atomic);
180#endif
181	    }
182	}
183	return FcFalse;
184    }
185    (void) unlink ((char *) atomic->new);
186    return FcTrue;
187}
188
189FcChar8 *
190FcAtomicNewFile (FcAtomic *atomic)
191{
192    return atomic->new;
193}
194
195FcChar8 *
196FcAtomicOrigFile (FcAtomic *atomic)
197{
198    return atomic->file;
199}
200
201FcBool
202FcAtomicReplaceOrig (FcAtomic *atomic)
203{
204#ifdef _WIN32
205    unlink ((const char *) atomic->file);
206#endif
207    if (rename ((char *) atomic->new, (char *) atomic->file) < 0)
208	return FcFalse;
209    return FcTrue;
210}
211
212void
213FcAtomicDeleteNew (FcAtomic *atomic)
214{
215    unlink ((char *) atomic->new);
216}
217
218void
219FcAtomicUnlock (FcAtomic *atomic)
220{
221#ifdef HAVE_LINK
222    if (unlink ((char *) atomic->lck) == -1)
223	rmdir ((char *) atomic->lck);
224#else
225    rmdir ((char *) atomic->lck);
226#endif
227}
228
229void
230FcAtomicDestroy (FcAtomic *atomic)
231{
232    free (atomic);
233}
234#define __fcatomic__
235#include "fcaliastail.h"
236#undef __fcatomic__
237