Coverage Report

Created: 2025-07-07 10:01

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