Bug Summary

File:utils/filename.c
Warning:line 371, column 9
The 1st argument to 'unlinkat' is between -99 and -1 but should be a valid file descriptor or AT_FDCWD

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name filename.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -resource-dir /usr/lib/llvm-19/lib/clang/19 -isystem /usr/include/mit-krb5 -I . -I include -I build/Linux-framebuffer -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /usr/include/x86_64-linux-gnu -I /usr/include/p11-kit-1 -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -I /usr/include/webp -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -D nsframebuffer -D small -D NETSURF_FB_RESPATH="${HOME}/.netsurf/:${NETSURFRES}:/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf:./frontends/framebuffer/res" -D NETSURF_FB_FONTPATH="/usr/share/fonts/truetype/dejavu:/usr/share/fonts/truetype/msttcorefonts" -D NETSURF_FB_FONT_SANS_SERIF="DejaVuSans.ttf" -D NETSURF_FB_FONT_SANS_SERIF_BOLD="DejaVuSans-Bold.ttf" -D NETSURF_FB_FONT_SANS_SERIF_ITALIC="DejaVuSans-Oblique.ttf" -D NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD="DejaVuSans-BoldOblique.ttf" -D NETSURF_FB_FONT_SERIF="DejaVuSerif.ttf" -D NETSURF_FB_FONT_SERIF_BOLD="DejaVuSerif-Bold.ttf" -D NETSURF_FB_FONT_MONOSPACE="DejaVuSansMono.ttf" -D NETSURF_FB_FONT_MONOSPACE_BOLD="DejaVuSansMono-Bold.ttf" -D NETSURF_FB_FONT_CURSIVE="Comic_Sans_MS.ttf" -D NETSURF_FB_FONT_FANTASY="Impact.ttf" -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D _POSIX_C_SOURCE=200809L -D _XOPEN_SOURCE=700 -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D DUK_OPT_HAVE_CUSTOM_H -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2025-11-28-143604-2569303-1 -x c utils/filename.c
1/*
2 * Copyright 2006 Richard Wilson <info@tinct.net>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/** \file
20 * Provides a central method of obtaining unique filenames.
21 *
22 * A maximum of 2^24 files can be allocated at any point in time.
23 */
24
25#include <assert.h>
26#include <sys/types.h>
27#include <stdbool.h>
28#include <string.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <errno(*__errno_location ()).h>
32#include <fcntl.h>
33#include <sys/stat.h>
34#include <unistd.h>
35
36#include "utils/dirent.h"
37#include "utils/errors.h"
38#include "utils/file.h"
39#include "utils/filename.h"
40#include "utils/log.h"
41#include "utils/utils.h"
42
43#define FULL_WORD(unsigned int)0xffffffffu (unsigned int)0xffffffffu
44#define START_PREFIX('0' + '0' * 10) ('0' + '0' * 10)
45
46struct directory {
47 int numeric_prefix; /** numeric representation of prefix */
48 char prefix[10]; /** directory prefix, eg '00/11/52/' */
49 unsigned int low_used; /** first 32 files, 1 bit per file */
50 unsigned int high_used; /** last 32 files, 1 bit per file */
51 struct directory *next; /** next directory (sorted by prefix) */
52};
53
54
55static struct directory *root = NULL((void*)0);
56static char filename_buffer[12];
57static char filename_directory[256];
58
59static struct directory *filename_create_directory(const char *prefix);
60static bool_Bool filename_flush_directory(const char *folder, int depth);
61
62/**
63 * Request a new, unique, filename.
64 *
65 * \return a pointer to a shared buffer containing the new filename,
66 * NULL on failure
67 */
68const char *filename_request(void)
69{
70 struct directory *dir;
71 int i = -1;
72
73 for (dir = root; dir; dir = dir->next) {
74 if ((dir->low_used & dir->high_used) != FULL_WORD(unsigned int)0xffffffffu) {
75 if (dir->low_used != FULL_WORD(unsigned int)0xffffffffu) {
76 for (i = 0; (dir->low_used & (1 << i)); i++);
77 } else {
78 for (i = 0; (dir->high_used & (1 << i)); i++);
79 i += 32;
80 }
81 break;
82 }
83 }
84
85 if (i == -1) {
86 /* no available slots - create a new directory */
87 dir = filename_create_directory(NULL((void*)0));
88 if (dir == NULL((void*)0)) {
89 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 90
, }; nslog__log(&_nslog_ctx, "Failed to create a new directory."
); } } while(0)
90 "Failed to create a new directory.")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 90
, }; nslog__log(&_nslog_ctx, "Failed to create a new directory."
); } } while(0)
;
91 return NULL((void*)0);
92 }
93 i = 63;
94 }
95
96 if (i < 32)
97 dir->low_used |= (1 << i);
98 else
99 dir->high_used |= (1 << (i - 32));
100
101 i = i % 99;
102
103 snprintf(filename_buffer, sizeof(filename_buffer), "%s%.2u", dir->prefix, (unsigned int)i);
104
105 return filename_buffer;
106}
107
108
109/**
110 * Claim a specific filename.
111 *
112 * \param filename the filename to claim
113 * \return whether the claim was successful
114 */
115bool_Bool filename_claim(const char *filename)
116{
117 char dir_prefix[9];
118 int file;
119 struct directory *dir;
120
121 /* filename format is always '01/23/45/XX' */
122 strncpy(dir_prefix, filename, 9);
123 dir_prefix[8] = '\0';
124 file = (filename[10] + filename[9] * 10 - START_PREFIX('0' + '0' * 10));
125
126 /* create the directory */
127 dir = filename_create_directory(dir_prefix);
128 if (dir == NULL((void*)0))
129 return false0;
130
131 /* update the entry */
132 if (file < 32) {
133 if (dir->low_used & (1 << file))
134 return false0;
135 dir->low_used |= (1 << file);
136 } else {
137 if (dir->high_used & (1 << (file - 32)))
138 return false0;
139 dir->high_used |= (1 << (file - 32));
140 }
141
142 return true1;
143}
144
145
146/**
147 * Releases a filename for future use.
148 *
149 * \param filename the filename to release
150 */
151void filename_release(const char *filename)
152{
153 struct directory *dir;
154 int index, file;
155
156 /* filename format is always '01/23/45/XX' */
157 index = ((filename[7] + filename[6] * 10 - START_PREFIX('0' + '0' * 10)) |
158 ((filename[4] + filename[3] * 10 - START_PREFIX('0' + '0' * 10)) << 6) |
159 ((filename[1] + filename[0] * 10 - START_PREFIX('0' + '0' * 10)) << 12));
160 file = (filename[10] + filename[9] * 10 - START_PREFIX('0' + '0' * 10));
161
162 /* modify the correct directory entry */
163 for (dir = root; dir; dir = dir->next) {
164 if (dir->numeric_prefix == index) {
165 if (file < 32)
166 dir->low_used &= ~(1 << file);
167 else
168 dir->high_used &= ~(1 << (file - 32));
169 return;
170 }
171 }
172}
173
174
175/**
176 * Initialise the filename provider.
177 */
178bool_Bool filename_initialise(void)
179{
180 char *directory, *start;
181 int ret;
182
183 directory = strdup(TEMP_FILENAME_PREFIX"/tmp/WWW/NetSurf/Misc");
184 if (directory == NULL((void*)0))
185 return false0;
186
187 for (start = directory; *start != '\0'; start++) {
188 if (*start == '/') {
189 *start = '\0';
190 NSLOG(netsurf, INFO, "Creating \"%s\"", directory)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 190
, }; nslog__log(&_nslog_ctx, "Creating \"%s\"", directory
); } } while(0)
;
191 ret = nsmkdir(directory, S_IRWXU)mkdir((directory), ((0400|0200|0100)));
192 if (ret != 0 && errno(*__errno_location ()) != EEXIST17) {
193 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 195
, }; nslog__log(&_nslog_ctx, "Failed to create directory \"%s\""
, directory); } } while(0)
194 "Failed to create directory \"%s\"",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 195
, }; nslog__log(&_nslog_ctx, "Failed to create directory \"%s\""
, directory); } } while(0)
195 directory)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 195
, }; nslog__log(&_nslog_ctx, "Failed to create directory \"%s\""
, directory); } } while(0)
;
196 free(directory);
197 return false0;
198 }
199
200 *start = '/';
201 }
202 }
203
204 NSLOG(netsurf, INFO, "Temporary directory location: %s", directory)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 204
, }; nslog__log(&_nslog_ctx, "Temporary directory location: %s"
, directory); } } while(0)
;
205 ret = nsmkdir(directory, S_IRWXU)mkdir((directory), ((0400|0200|0100)));
206
207 free(directory);
208
209 if (ret != 0) {
210 return false0;
211 }
212 return true1;
213}
214
215
216/**
217 * Deletes all files in the cache directory that are not accounted for.
218 */
219void filename_flush(void)
1
[debug] analyzing from filename_flush
220{
221 while (filename_flush_directory(TEMP_FILENAME_PREFIX"/tmp/WWW/NetSurf/Misc", 0));
2
Calling 'filename_flush_directory'
222}
223
224
225/**
226 * Deletes some files in a directory that are not accounted for.
227 *
228 * A single call to this function may not delete all the files in
229 * a directory. It should be called until it returns false.
230 *
231 * \param folder the folder to search
232 * \param depth the folder depth
233 * \returns whether further calls may be needed
234 */
235bool_Bool filename_flush_directory(const char *folder, int depth)
236{
237 DIR *parent;
238 struct dirent *entry;
239 bool_Bool changed = false0;
240 bool_Bool del;
241 int number, i;
242 int prefix = 0;
243 unsigned int prefix_mask = (0x3f << 12);
244 char child[256];
245 const char *prefix_start = NULL((void*)0);
246 struct directory *dir = NULL((void*)0);
247
248 /* Maximum permissible depth is 3 */
249 assert(depth <= 3)((depth <= 3) ? (void) (0) : __assert_fail ("depth <= 3"
, "utils/filename.c", 249, __extension__ __PRETTY_FUNCTION__)
)
;
3
'?' condition is true
250
251 if (depth
3.1
'depth' is <= 0
> 0) {
4
Taking false branch
252 /* Not a top-level directory, so determine the prefix
253 * by removing the last /XX component */
254 prefix_start = folder + strlen(folder) - depth * 3 + 1;
255 }
256
257 /* Calculate the numeric prefix */
258 for (i = 0; i < depth; i++) {
5
Loop condition is false. Execution continues on line 266
259 number = prefix_start[1] + prefix_start[0] * 10 - START_PREFIX('0' + '0' * 10);
260 prefix |= (number << (12 - i * 6));
261 prefix_mask |= (0x3f << (12 - i * 6));
262 prefix_start += 3;
263 }
264
265 /* If we're flushing a leaf directory, find it in the list */
266 if (depth
5.1
'depth' is not equal to 3
== 3) {
6
Taking false branch
267 for (dir = root; dir; dir = dir->next) {
268 if (dir->numeric_prefix == prefix)
269 break;
270 }
271
272 if (dir == NULL((void*)0))
273 return false0;
274 }
275
276 parent = opendir(folder);
277 if (parent
6.1
'parent' is not equal to NULL
== NULL((void*)0))
7
Taking false branch
278 return false0;
279
280 while ((entry = readdir(parent))) {
8
Loop condition is true. Entering loop body
281 int written;
282 struct stat statbuf;
283
284 /* Ignore '.' and '..' */
285 if (strcmp(entry->d_name, ".") == 0 ||
9
Assuming the condition is false
11
Taking false branch
286 strcmp(entry->d_name, "..") == 0)
10
Assuming the condition is false
287 continue;
288
289 written = snprintf(child, sizeof(child), "%s/%s",
290 folder, entry->d_name);
291 if (written == sizeof(child)) {
12
Assuming the condition is false
13
Taking false branch
292 child[sizeof(child) - 1] = '\0';
293 }
294
295#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
296 if (fstatat(dirfd(parent), entry->d_name, &statbuf,
14
Taking false branch
297 AT_SYMLINK_NOFOLLOW0x100) == -1) {
298#else
299 if (stat(child, &statbuf) == -1) {
300#endif
301 NSLOG(netsurf, INFO, "Unable to stat %s: %s", child,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 302
, }; nslog__log(&_nslog_ctx, "Unable to stat %s: %s", child
, strerror((*__errno_location ()))); } } while(0)
302 strerror(errno))do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 302
, }; nslog__log(&_nslog_ctx, "Unable to stat %s: %s", child
, strerror((*__errno_location ()))); } } while(0)
;
303 continue;
304 }
305
306 /* first 3 depths are directories only, then files only */
307 if (depth
14.1
'depth' is < 3
< 3) {
15
Taking true branch
308 /* Delete any unexpected files */
309 del = !S_ISDIR(statbuf.st_mode)((((statbuf.st_mode)) & 0170000) == (0040000));
16
Assuming the condition is false
310 } else {
311 /* Delete any unexpected directories */
312 del = S_ISDIR(statbuf.st_mode)((((statbuf.st_mode)) & 0170000) == (0040000));
313 }
314
315 /* check we are a file numbered '00' -> '63' */
316 if (del
16.1
'del' is not equal to false
== false0 && (entry->d_name[0] >= '0') &&
317 (entry->d_name[0] <= '6') &&
318 (entry->d_name[1] >= '0') &&
319 (entry->d_name[1] <= '9') &&
320 (entry->d_name[2] == '\0')) {
321 number = atoi(entry->d_name);
322
323 if (number >= 0 && number <= 63) {
324 if (depth == 3) {
325 /* File: delete if not in bitfield */
326 if (number < 32)
327 del = !(dir->low_used &
328 (1 << number));
329 else
330 del = !(dir->high_used &
331 (1 << (number - 32)));
332 } else {
333 /* Directory: delete unless in list */
334 del = true1;
335
336 /* Insert into numeric prefix */
337 prefix &= ~(0x3f << (12 - depth * 6));
338 prefix |= (number << (12 - depth * 6));
339
340 /* Find in dir list */
341 for (dir = root; dir; dir = dir->next) {
342 number = dir->numeric_prefix &
343 prefix_mask;
344 if (number == prefix) {
345 /* In list: retain */
346 del = false0;
347 break;
348 }
349 }
350 }
351 } else {
352 /* Unexpected name: delete */
353 del = true1;
354 }
355 } else {
356 /* Unexpected name: delete */
357 del = true1;
358 }
359
360 /* continue if this is a file we want to retain */
361 if (del
16.2
'del' is not equal to false
== false0 && (!S_ISDIR(statbuf.st_mode)((((statbuf.st_mode)) & 0170000) == (0040000))))
362 continue;
363
364 /* delete or recurse */
365 if (del
16.3
'del' is true
) {
17
Taking true branch
366 if (S_ISDIR(statbuf.st_mode)((((statbuf.st_mode)) & 0170000) == (0040000))) {
18
Taking false branch
367 changed = (netsurf_recursive_rm(child) ==
368 NSERROR_OK);
369 } else {
370#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
371 if (unlinkat(dirfd(parent), entry->d_name, 0)) {
19
Assuming that 'dirfd' fails
20
The 1st argument to 'unlinkat' is between -99 and -1 but should be a valid file descriptor or AT_FDCWD
372#else
373 if (unlink(child)) {
374#endif
375 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 376
, }; nslog__log(&_nslog_ctx, "Failed to remove '%s'", child
); } } while(0)
376 "Failed to remove '%s'", child)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 376
, }; nslog__log(&_nslog_ctx, "Failed to remove '%s'", child
); } } while(0)
;
377 } else
378 changed = true1;
379 }
380 } else {
381 while (filename_flush_directory(child, depth + 1));
382 }
383 }
384
385 closedir(parent);
386
387 return changed;
388}
389
390
391/**
392 * Creates a new directory.
393 *
394 * \param prefix the prefix to use, or NULL to allocate a new one
395 * \return a new directory structure, or NULL on memory exhaustion or
396 * creation failure
397 *
398 * Empty directories are never deleted, except by an explicit call to
399 * filename_flush().
400 */
401static struct directory *filename_create_directory(const char *prefix)
402{
403 char *last_1, *last_2;
404 int index;
405 struct directory *old_dir, *new_dir, *prev_dir = NULL((void*)0);
406 char dir_prefix[16];
407 int i;
408
409 /* get the lowest unique prefix, or use the provided one */
410 if (prefix == NULL((void*)0)) {
411 for (index = 0, old_dir = root; old_dir;
412 index++, old_dir = old_dir->next) {
413 if (old_dir->numeric_prefix != index)
414 break;
415
416 prev_dir = old_dir;
417 }
418
419 sprintf(dir_prefix, "%.2i/%.2i/%.2i/",
420 ((index >> 12) & 63),
421 ((index >> 6) & 63),
422 ((index >> 0) & 63));
423
424 prefix = dir_prefix;
425 } else {
426 /* prefix format is always '01/23/45/' */
427 index = ((prefix[7] + prefix[6] * 10 - START_PREFIX('0' + '0' * 10)) |
428 ((prefix[4] + prefix[3] * 10 - START_PREFIX('0' + '0' * 10)) << 6) |
429 ((prefix[1] + prefix[0] * 10 - START_PREFIX('0' + '0' * 10)) << 12));
430
431 for (old_dir = root; old_dir; old_dir = old_dir->next) {
432 if (old_dir->numeric_prefix == index)
433 return old_dir;
434
435 else if (old_dir->numeric_prefix > index)
436 break;
437
438 prev_dir = old_dir;
439 }
440 }
441
442 /* allocate a new directory */
443 new_dir = malloc(sizeof(struct directory));
444 if (new_dir == NULL((void*)0)) {
445 NSLOG(netsurf, INFO, "No memory for malloc()")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 445
, }; nslog__log(&_nslog_ctx, "No memory for malloc()"); }
} while(0)
;
446 return NULL((void*)0);
447 }
448
449 strncpy(new_dir->prefix, prefix, 9);
450 new_dir->prefix[9] = '\0';
451 new_dir->low_used = new_dir->high_used = 0;
452 new_dir->numeric_prefix = index;
453
454 if (prev_dir == NULL((void*)0)) {
455 new_dir->next = root;
456 root = new_dir;
457 } else {
458 new_dir->next = prev_dir->next;
459 prev_dir->next = new_dir;
460 }
461
462 /* if the previous directory has the same parent then we can simply
463 * create the child. */
464 if (prev_dir && strncmp(prev_dir->prefix, new_dir->prefix, 6) == 0) {
465 new_dir->prefix[8] = '\0';
466 sprintf(filename_directory, "%s/%s",
467 TEMP_FILENAME_PREFIX"/tmp/WWW/NetSurf/Misc",
468 new_dir->prefix);
469 new_dir->prefix[8] = '/';
470
471 if (!is_dir(filename_directory)) {
472 if (!nsmkdir(filename_directory, S_IRWXU)mkdir((filename_directory), ((0400|0200|0100))))
473 return new_dir;
474
475 /* the user has probably deleted the parent directory
476 * whilst we are running if there is an error, so we
477 * don't report this yet and try to create the
478 * structure normally. */
479 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 481
, }; nslog__log(&_nslog_ctx, "Failed to create optimised structure '%s'"
, filename_directory); } } while(0)
480 "Failed to create optimised structure '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 481
, }; nslog__log(&_nslog_ctx, "Failed to create optimised structure '%s'"
, filename_directory); } } while(0)
481 filename_directory)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 481
, }; nslog__log(&_nslog_ctx, "Failed to create optimised structure '%s'"
, filename_directory); } } while(0)
;
482 }
483 }
484
485 /* create the directory structure */
486 sprintf(filename_directory, "%s/", TEMP_FILENAME_PREFIX"/tmp/WWW/NetSurf/Misc");
487 last_1 = filename_directory + SLEN(TEMP_FILENAME_PREFIX)(sizeof(("/tmp/WWW/NetSurf/Misc")) - 1) + 1;
488 last_2 = new_dir->prefix;
489
490 /* create each subdirectory, up to the maximum depth of 3 */
491 for (i = 0; i < 3 && *last_2; i++) {
492 *last_1++ = *last_2++;
493 while (*last_2 && *last_2 != '/')
494 *last_1++ = *last_2++;
495
496 if (*last_2) {
497 last_1[0] = '\0';
498
499 if (!is_dir(filename_directory)) {
500 if (nsmkdir(filename_directory, S_IRWXU)mkdir((filename_directory), ((0400|0200|0100)))) {
501 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 503
, }; nslog__log(&_nslog_ctx, "Failed to create directory '%s'"
, filename_directory); } } while(0)
502 "Failed to create directory '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 503
, }; nslog__log(&_nslog_ctx, "Failed to create directory '%s'"
, filename_directory); } } while(0)
503 filename_directory)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "utils/filename.c", sizeof("utils/filename.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 503
, }; nslog__log(&_nslog_ctx, "Failed to create directory '%s'"
, filename_directory); } } while(0)
;
504 return NULL((void*)0);
505 }
506 }
507 }
508 }
509
510 return new_dir;
511}