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
73 FcAtomic *
74 FcAtomicCreate (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
105 FcBool
106 FcAtomicLock (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
189 FcChar8 *
190 FcAtomicNewFile (FcAtomic *atomic)
191 {
192 return atomic->new;
193 }
194
195 FcChar8 *
196 FcAtomicOrigFile (FcAtomic *atomic)
197 {
198 return atomic->file;
199 }
200
201 FcBool
202 FcAtomicReplaceOrig (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
212 void
213 FcAtomicDeleteNew (FcAtomic *atomic)
214 {
215 unlink ((char *) atomic->new);
216 }
217
218 void
219 FcAtomicUnlock (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
229 void
230 FcAtomicDestroy (FcAtomic *atomic)
231 {
232 if (atomic)
233 free (atomic);
234 }
235 #define __fcatomic__
236 #include "fcaliastail.h"
237 #undef __fcatomic__