350744abb4d8a1fab7501c702a45c0c99706abad
[platform/upstream/fontconfig.git] / src / fcatomic.c
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 #include "fcint.h"
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <fcntl.h>
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <time.h>
57
58 #ifdef _WIN32
59 #define mkdir(path,mode) _mkdir(path)
60 #endif
61
62 #define NEW_NAME        ".NEW"
63 #define LCK_NAME        ".LCK"
64 #define TMP_NAME        ".TMP-XXXXXX"
65
66 FcAtomic *
67 FcAtomicCreate (const FcChar8   *file)
68 {
69     int     file_len = strlen ((char *) file);
70     int     new_len = file_len + sizeof (NEW_NAME);
71     int     lck_len = file_len + sizeof (LCK_NAME);
72     int     tmp_len = file_len + sizeof (TMP_NAME);
73     int     total_len = (sizeof (FcAtomic) +
74                          file_len + 1 +
75                          new_len + 1 +
76                          lck_len + 1 +
77                          tmp_len + 1);
78     FcAtomic    *atomic = malloc (total_len);
79     if (!atomic)
80         return 0;
81     FcMemAlloc (FC_MEM_ATOMIC, total_len);
82
83     atomic->file = (FcChar8 *) (atomic + 1);
84     strcpy ((char *) atomic->file, (char *) file);
85
86     atomic->new = atomic->file + file_len + 1;
87     strcpy ((char *) atomic->new, (char *) file);
88     strcat ((char *) atomic->new, NEW_NAME);
89
90     atomic->lck = atomic->new + new_len + 1;
91     strcpy ((char *) atomic->lck, (char *) file);
92     strcat ((char *) atomic->lck, LCK_NAME);
93
94     atomic->tmp = atomic->lck + lck_len + 1;
95
96     return atomic;
97 }
98
99 FcBool
100 FcAtomicLock (FcAtomic *atomic)
101 {
102     int         ret;
103     struct stat lck_stat;
104
105 #ifdef HAVE_LINK
106     int         fd = -1;
107     FILE        *f = 0;
108     FcBool      no_link = FcFalse;
109
110     strcpy ((char *) atomic->tmp, (char *) atomic->file);
111     strcat ((char *) atomic->tmp, TMP_NAME);
112     fd = mkstemp ((char *) atomic->tmp);
113     if (fd < 0)
114         return FcFalse;
115     f = fdopen (fd, "w");
116     if (!f)
117     {
118         close (fd);
119         unlink ((char *) atomic->tmp);
120         return FcFalse;
121     }
122     ret = fprintf (f, "%ld\n", (long)getpid());
123     if (ret <= 0)
124     {
125         fclose (f);
126         unlink ((char *) atomic->tmp);
127         return FcFalse;
128     }
129     if (fclose (f) == EOF)
130     {
131         unlink ((char *) atomic->tmp);
132         return FcFalse;
133     }
134     ret = link ((char *) atomic->tmp, (char *) atomic->lck);
135     if (ret < 0 && errno == EPERM)
136     {
137         /* the filesystem where atomic->lck points to may not supports
138          * the hard link. so better try to fallback
139          */
140         ret = mkdir ((char *) atomic->lck, 0600);
141         no_link = FcTrue;
142     }
143     (void) unlink ((char *) atomic->tmp);
144 #else
145     ret = mkdir ((char *) atomic->lck, 0600);
146 #endif
147     if (ret < 0)
148     {
149         /*
150          * If the file is around and old (> 10 minutes),
151          * assume the lock is stale.  This assumes that any
152          * machines sharing the same filesystem will have clocks
153          * reasonably close to each other.
154          */
155         if (FcStat (atomic->lck, &lck_stat) >= 0)
156         {
157             time_t  now = time (0);
158             if ((long int) (now - lck_stat.st_mtime) > 10 * 60)
159             {
160 #ifdef HAVE_LINK
161                 if (no_link)
162                 {
163                     if (rmdir ((char *) atomic->lck) == 0)
164                         return FcAtomicLock (atomic);
165                 }
166                 else
167                 {
168                     if (unlink ((char *) atomic->lck) == 0)
169                         return FcAtomicLock (atomic);
170                 }
171 #else
172                 if (rmdir ((char *) atomic->lck) == 0)
173                     return FcAtomicLock (atomic);
174 #endif
175             }
176         }
177         return FcFalse;
178     }
179     (void) unlink ((char *) atomic->new);
180     return FcTrue;
181 }
182
183 FcChar8 *
184 FcAtomicNewFile (FcAtomic *atomic)
185 {
186     return atomic->new;
187 }
188
189 FcChar8 *
190 FcAtomicOrigFile (FcAtomic *atomic)
191 {
192     return atomic->file;
193 }
194
195 FcBool
196 FcAtomicReplaceOrig (FcAtomic *atomic)
197 {
198 #ifdef _WIN32
199     unlink ((const char *) atomic->file);
200 #endif
201     if (rename ((char *) atomic->new, (char *) atomic->file) < 0)
202         return FcFalse;
203     return FcTrue;
204 }
205
206 void
207 FcAtomicDeleteNew (FcAtomic *atomic)
208 {
209     unlink ((char *) atomic->new);
210 }
211
212 void
213 FcAtomicUnlock (FcAtomic *atomic)
214 {
215 #ifdef HAVE_LINK
216     if (unlink ((char *) atomic->lck) == -1)
217         rmdir ((char *) atomic->lck);
218 #else
219     rmdir ((char *) atomic->lck);
220 #endif
221 }
222
223 void
224 FcAtomicDestroy (FcAtomic *atomic)
225 {
226     FcMemFree (FC_MEM_ATOMIC, sizeof (FcAtomic) +
227                strlen ((char *) atomic->file) * 4 + 4 +
228                sizeof (NEW_NAME) + sizeof (LCK_NAME) +
229                sizeof (TMP_NAME));
230
231     free (atomic);
232 }
233 #define __fcatomic__
234 #include "fcaliastail.h"
235 #undef __fcatomic__