summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-05-20 15:56:47 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-05-20 15:56:47 (UTC)
commitdd7c172542440170b5b1aca8be43d2ad6dae7227 (patch) (unidiff)
treed991f45be79cec1d3d031bac70413146f593a018
parentaf2e75616d1bfb7dc79d299d10ae0bd39bef47bc (diff)
downloadcgit-dd7c172542440170b5b1aca8be43d2ad6dae7227.zip
cgit-dd7c172542440170b5b1aca8be43d2ad6dae7227.tar.gz
cgit-dd7c172542440170b5b1aca8be43d2ad6dae7227.tar.bz2
cache.c: fix error checking in print_slot()
The change to print_slot() in cdc6b2f8e7a8d43dcfe0475a9d3498333ea686b8 made the function return correct errno for read errors while ignoring write errors, which is not what was intended. This patch tries to rectify things. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cache.c11
1 files changed, 7 insertions, 4 deletions
diff --git a/cache.c b/cache.c
index aa97ae1..9f02cf5 100644
--- a/cache.c
+++ b/cache.c
@@ -1,436 +1,439 @@
1/* cache.c: cache management 1/* cache.c: cache management
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 * 7 *
8 * 8 *
9 * The cache is just a directory structure where each file is a cache slot, 9 * The cache is just a directory structure where each file is a cache slot,
10 * and each filename is based on the hash of some key (e.g. the cgit url). 10 * and each filename is based on the hash of some key (e.g. the cgit url).
11 * Each file contains the full key followed by the cached content for that 11 * Each file contains the full key followed by the cached content for that
12 * key. 12 * key.
13 * 13 *
14 */ 14 */
15 15
16#include "cgit.h" 16#include "cgit.h"
17#include "cache.h" 17#include "cache.h"
18 18
19#define CACHE_BUFSIZE (1024 * 4) 19#define CACHE_BUFSIZE (1024 * 4)
20 20
21struct cache_slot { 21struct cache_slot {
22 const char *key; 22 const char *key;
23 int keylen; 23 int keylen;
24 int ttl; 24 int ttl;
25 cache_fill_fn fn; 25 cache_fill_fn fn;
26 void *cbdata; 26 void *cbdata;
27 int cache_fd; 27 int cache_fd;
28 int lock_fd; 28 int lock_fd;
29 const char *cache_name; 29 const char *cache_name;
30 const char *lock_name; 30 const char *lock_name;
31 int match; 31 int match;
32 struct stat cache_st; 32 struct stat cache_st;
33 struct stat lock_st; 33 struct stat lock_st;
34 int bufsize; 34 int bufsize;
35 char buf[CACHE_BUFSIZE]; 35 char buf[CACHE_BUFSIZE];
36}; 36};
37 37
38/* Open an existing cache slot and fill the cache buffer with 38/* Open an existing cache slot and fill the cache buffer with
39 * (part of) the content of the cache file. Return 0 on success 39 * (part of) the content of the cache file. Return 0 on success
40 * and errno otherwise. 40 * and errno otherwise.
41 */ 41 */
42static int open_slot(struct cache_slot *slot) 42static int open_slot(struct cache_slot *slot)
43{ 43{
44 char *bufz; 44 char *bufz;
45 int bufkeylen = -1; 45 int bufkeylen = -1;
46 46
47 slot->cache_fd = open(slot->cache_name, O_RDONLY); 47 slot->cache_fd = open(slot->cache_name, O_RDONLY);
48 if (slot->cache_fd == -1) 48 if (slot->cache_fd == -1)
49 return errno; 49 return errno;
50 50
51 if (fstat(slot->cache_fd, &slot->cache_st)) 51 if (fstat(slot->cache_fd, &slot->cache_st))
52 return errno; 52 return errno;
53 53
54 slot->bufsize = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); 54 slot->bufsize = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
55 if (slot->bufsize < 0) 55 if (slot->bufsize < 0)
56 return errno; 56 return errno;
57 57
58 bufz = memchr(slot->buf, 0, slot->bufsize); 58 bufz = memchr(slot->buf, 0, slot->bufsize);
59 if (bufz) 59 if (bufz)
60 bufkeylen = bufz - slot->buf; 60 bufkeylen = bufz - slot->buf;
61 61
62 slot->match = bufkeylen == slot->keylen && 62 slot->match = bufkeylen == slot->keylen &&
63 !memcmp(slot->key, slot->buf, bufkeylen + 1); 63 !memcmp(slot->key, slot->buf, bufkeylen + 1);
64 64
65 return 0; 65 return 0;
66} 66}
67 67
68/* Close the active cache slot */ 68/* Close the active cache slot */
69static int close_slot(struct cache_slot *slot) 69static int close_slot(struct cache_slot *slot)
70{ 70{
71 int err = 0; 71 int err = 0;
72 if (slot->cache_fd > 0) { 72 if (slot->cache_fd > 0) {
73 if (close(slot->cache_fd)) 73 if (close(slot->cache_fd))
74 err = errno; 74 err = errno;
75 else 75 else
76 slot->cache_fd = -1; 76 slot->cache_fd = -1;
77 } 77 }
78 return err; 78 return err;
79} 79}
80 80
81/* Print the content of the active cache slot (but skip the key). */ 81/* Print the content of the active cache slot (but skip the key). */
82static int print_slot(struct cache_slot *slot) 82static int print_slot(struct cache_slot *slot)
83{ 83{
84 ssize_t i; 84 ssize_t i, j;
85 85
86 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); 86 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
87 if (i != slot->keylen + 1) 87 if (i != slot->keylen + 1)
88 return errno; 88 return errno;
89 89
90 while((i = xread(slot->cache_fd, slot->buf, sizeof(slot->buf))) > 0) 90 do {
91 i = xwrite(STDOUT_FILENO, slot->buf, i); 91 i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
92 if (i > 0)
93 j = xwrite(STDOUT_FILENO, slot->buf, i);
94 } while (i > 0 && j == i);
92 95
93 if (i < 0) 96 if (i < 0 || j != i)
94 return errno; 97 return errno;
95 else 98 else
96 return 0; 99 return 0;
97} 100}
98 101
99/* Check if the slot has expired */ 102/* Check if the slot has expired */
100static int is_expired(struct cache_slot *slot) 103static int is_expired(struct cache_slot *slot)
101{ 104{
102 if (slot->ttl < 0) 105 if (slot->ttl < 0)
103 return 0; 106 return 0;
104 else 107 else
105 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL); 108 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL);
106} 109}
107 110
108/* Check if the slot has been modified since we opened it. 111/* Check if the slot has been modified since we opened it.
109 * NB: If stat() fails, we pretend the file is modified. 112 * NB: If stat() fails, we pretend the file is modified.
110 */ 113 */
111static int is_modified(struct cache_slot *slot) 114static int is_modified(struct cache_slot *slot)
112{ 115{
113 struct stat st; 116 struct stat st;
114 117
115 if (stat(slot->cache_name, &st)) 118 if (stat(slot->cache_name, &st))
116 return 1; 119 return 1;
117 return (st.st_ino != slot->cache_st.st_ino || 120 return (st.st_ino != slot->cache_st.st_ino ||
118 st.st_mtime != slot->cache_st.st_mtime || 121 st.st_mtime != slot->cache_st.st_mtime ||
119 st.st_size != slot->cache_st.st_size); 122 st.st_size != slot->cache_st.st_size);
120} 123}
121 124
122/* Close an open lockfile */ 125/* Close an open lockfile */
123static int close_lock(struct cache_slot *slot) 126static int close_lock(struct cache_slot *slot)
124{ 127{
125 int err = 0; 128 int err = 0;
126 if (slot->lock_fd > 0) { 129 if (slot->lock_fd > 0) {
127 if (close(slot->lock_fd)) 130 if (close(slot->lock_fd))
128 err = errno; 131 err = errno;
129 else 132 else
130 slot->lock_fd = -1; 133 slot->lock_fd = -1;
131 } 134 }
132 return err; 135 return err;
133} 136}
134 137
135/* Create a lockfile used to store the generated content for a cache 138/* Create a lockfile used to store the generated content for a cache
136 * slot, and write the slot key + \0 into it. 139 * slot, and write the slot key + \0 into it.
137 * Returns 0 on success and errno otherwise. 140 * Returns 0 on success and errno otherwise.
138 */ 141 */
139static int lock_slot(struct cache_slot *slot) 142static int lock_slot(struct cache_slot *slot)
140{ 143{
141 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL, 144 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL,
142 S_IRUSR|S_IWUSR); 145 S_IRUSR|S_IWUSR);
143 if (slot->lock_fd == -1) 146 if (slot->lock_fd == -1)
144 return errno; 147 return errno;
145 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) 148 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0)
146 return errno; 149 return errno;
147 return 0; 150 return 0;
148} 151}
149 152
150/* Release the current lockfile. If `replace_old_slot` is set the 153/* Release the current lockfile. If `replace_old_slot` is set the
151 * lockfile replaces the old cache slot, otherwise the lockfile is 154 * lockfile replaces the old cache slot, otherwise the lockfile is
152 * just deleted. 155 * just deleted.
153 */ 156 */
154static int unlock_slot(struct cache_slot *slot, int replace_old_slot) 157static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
155{ 158{
156 int err; 159 int err;
157 160
158 if (replace_old_slot) 161 if (replace_old_slot)
159 err = rename(slot->lock_name, slot->cache_name); 162 err = rename(slot->lock_name, slot->cache_name);
160 else 163 else
161 err = unlink(slot->lock_name); 164 err = unlink(slot->lock_name);
162 165
163 if (err) 166 if (err)
164 return errno; 167 return errno;
165 168
166 return 0; 169 return 0;
167} 170}
168 171
169/* Generate the content for the current cache slot by redirecting 172/* Generate the content for the current cache slot by redirecting
170 * stdout to the lock-fd and invoking the callback function 173 * stdout to the lock-fd and invoking the callback function
171 */ 174 */
172static int fill_slot(struct cache_slot *slot) 175static int fill_slot(struct cache_slot *slot)
173{ 176{
174 int tmp; 177 int tmp;
175 178
176 /* Preserve stdout */ 179 /* Preserve stdout */
177 tmp = dup(STDOUT_FILENO); 180 tmp = dup(STDOUT_FILENO);
178 if (tmp == -1) 181 if (tmp == -1)
179 return errno; 182 return errno;
180 183
181 /* Redirect stdout to lockfile */ 184 /* Redirect stdout to lockfile */
182 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1) 185 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
183 return errno; 186 return errno;
184 187
185 /* Generate cache content */ 188 /* Generate cache content */
186 slot->fn(slot->cbdata); 189 slot->fn(slot->cbdata);
187 190
188 /* Restore stdout */ 191 /* Restore stdout */
189 if (dup2(tmp, STDOUT_FILENO) == -1) 192 if (dup2(tmp, STDOUT_FILENO) == -1)
190 return errno; 193 return errno;
191 194
192 /* Close the temporary filedescriptor */ 195 /* Close the temporary filedescriptor */
193 if (close(tmp)) 196 if (close(tmp))
194 return errno; 197 return errno;
195 198
196 return 0; 199 return 0;
197} 200}
198 201
199/* Crude implementation of 32-bit FNV-1 hash algorithm, 202/* Crude implementation of 32-bit FNV-1 hash algorithm,
200 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details 203 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details
201 * about the magic numbers. 204 * about the magic numbers.
202 */ 205 */
203#define FNV_OFFSET 0x811c9dc5 206#define FNV_OFFSET 0x811c9dc5
204#define FNV_PRIME 0x01000193 207#define FNV_PRIME 0x01000193
205 208
206unsigned long hash_str(const char *str) 209unsigned long hash_str(const char *str)
207{ 210{
208 unsigned long h = FNV_OFFSET; 211 unsigned long h = FNV_OFFSET;
209 unsigned char *s = (unsigned char *)str; 212 unsigned char *s = (unsigned char *)str;
210 213
211 if (!s) 214 if (!s)
212 return h; 215 return h;
213 216
214 while(*s) { 217 while(*s) {
215 h *= FNV_PRIME; 218 h *= FNV_PRIME;
216 h ^= *s++; 219 h ^= *s++;
217 } 220 }
218 return h; 221 return h;
219} 222}
220 223
221static int process_slot(struct cache_slot *slot) 224static int process_slot(struct cache_slot *slot)
222{ 225{
223 int err; 226 int err;
224 227
225 err = open_slot(slot); 228 err = open_slot(slot);
226 if (!err && slot->match) { 229 if (!err && slot->match) {
227 if (is_expired(slot)) { 230 if (is_expired(slot)) {
228 if (!lock_slot(slot)) { 231 if (!lock_slot(slot)) {
229 /* If the cachefile has been replaced between 232 /* If the cachefile has been replaced between
230 * `open_slot` and `lock_slot`, we'll just 233 * `open_slot` and `lock_slot`, we'll just
231 * serve the stale content from the original 234 * serve the stale content from the original
232 * cachefile. This way we avoid pruning the 235 * cachefile. This way we avoid pruning the
233 * newly generated slot. The same code-path 236 * newly generated slot. The same code-path
234 * is chosen if fill_slot() fails for some 237 * is chosen if fill_slot() fails for some
235 * reason. 238 * reason.
236 * 239 *
237 * TODO? check if the new slot contains the 240 * TODO? check if the new slot contains the
238 * same key as the old one, since we would 241 * same key as the old one, since we would
239 * prefer to serve the newest content. 242 * prefer to serve the newest content.
240 * This will require us to open yet another 243 * This will require us to open yet another
241 * file-descriptor and read and compare the 244 * file-descriptor and read and compare the
242 * key from the new file, so for now we're 245 * key from the new file, so for now we're
243 * lazy and just ignore the new file. 246 * lazy and just ignore the new file.
244 */ 247 */
245 if (is_modified(slot) || fill_slot(slot)) { 248 if (is_modified(slot) || fill_slot(slot)) {
246 unlock_slot(slot, 0); 249 unlock_slot(slot, 0);
247 close_lock(slot); 250 close_lock(slot);
248 } else { 251 } else {
249 close_slot(slot); 252 close_slot(slot);
250 unlock_slot(slot, 1); 253 unlock_slot(slot, 1);
251 slot->cache_fd = slot->lock_fd; 254 slot->cache_fd = slot->lock_fd;
252 } 255 }
253 } 256 }
254 } 257 }
255 if ((err = print_slot(slot)) != 0) { 258 if ((err = print_slot(slot)) != 0) {
256 cache_log("[cgit] error printing cache %s: %s (%d)\n", 259 cache_log("[cgit] error printing cache %s: %s (%d)\n",
257 slot->cache_name, 260 slot->cache_name,
258 strerror(err), 261 strerror(err),
259 err); 262 err);
260 } 263 }
261 close_slot(slot); 264 close_slot(slot);
262 return err; 265 return err;
263 } 266 }
264 267
265 /* If the cache slot does not exist (or its key doesn't match the 268 /* If the cache slot does not exist (or its key doesn't match the
266 * current key), lets try to create a new cache slot for this 269 * current key), lets try to create a new cache slot for this
267 * request. If this fails (for whatever reason), lets just generate 270 * request. If this fails (for whatever reason), lets just generate
268 * the content without caching it and fool the caller to belive 271 * the content without caching it and fool the caller to belive
269 * everything worked out (but print a warning on stdout). 272 * everything worked out (but print a warning on stdout).
270 */ 273 */
271 274
272 close_slot(slot); 275 close_slot(slot);
273 if ((err = lock_slot(slot)) != 0) { 276 if ((err = lock_slot(slot)) != 0) {
274 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", 277 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
275 slot->lock_name, strerror(err), err); 278 slot->lock_name, strerror(err), err);
276 slot->fn(slot->cbdata); 279 slot->fn(slot->cbdata);
277 return 0; 280 return 0;
278 } 281 }
279 282
280 if ((err = fill_slot(slot)) != 0) { 283 if ((err = fill_slot(slot)) != 0) {
281 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n", 284 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n",
282 slot->lock_name, strerror(err), err); 285 slot->lock_name, strerror(err), err);
283 unlock_slot(slot, 0); 286 unlock_slot(slot, 0);
284 close_lock(slot); 287 close_lock(slot);
285 slot->fn(slot->cbdata); 288 slot->fn(slot->cbdata);
286 return 0; 289 return 0;
287 } 290 }
288 // We've got a valid cache slot in the lock file, which 291 // We've got a valid cache slot in the lock file, which
289 // is about to replace the old cache slot. But if we 292 // is about to replace the old cache slot. But if we
290 // release the lockfile and then try to open the new cache 293 // release the lockfile and then try to open the new cache
291 // slot, we might get a race condition with a concurrent 294 // slot, we might get a race condition with a concurrent
292 // writer for the same cache slot (with a different key). 295 // writer for the same cache slot (with a different key).
293 // Lets avoid such a race by just printing the content of 296 // Lets avoid such a race by just printing the content of
294 // the lock file. 297 // the lock file.
295 slot->cache_fd = slot->lock_fd; 298 slot->cache_fd = slot->lock_fd;
296 unlock_slot(slot, 1); 299 unlock_slot(slot, 1);
297 if ((err = print_slot(slot)) != 0) { 300 if ((err = print_slot(slot)) != 0) {
298 cache_log("[cgit] error printing cache %s: %s (%d)\n", 301 cache_log("[cgit] error printing cache %s: %s (%d)\n",
299 slot->cache_name, 302 slot->cache_name,
300 strerror(err), 303 strerror(err),
301 err); 304 err);
302 } 305 }
303 close_slot(slot); 306 close_slot(slot);
304 return err; 307 return err;
305} 308}
306 309
307/* Print cached content to stdout, generate the content if necessary. */ 310/* Print cached content to stdout, generate the content if necessary. */
308int cache_process(int size, const char *path, const char *key, int ttl, 311int cache_process(int size, const char *path, const char *key, int ttl,
309 cache_fill_fn fn, void *cbdata) 312 cache_fill_fn fn, void *cbdata)
310{ 313{
311 unsigned long hash; 314 unsigned long hash;
312 int len, i; 315 int len, i;
313 char filename[1024]; 316 char filename[1024];
314 char lockname[1024 + 5]; /* 5 = ".lock" */ 317 char lockname[1024 + 5]; /* 5 = ".lock" */
315 struct cache_slot slot; 318 struct cache_slot slot;
316 319
317 /* If the cache is disabled, just generate the content */ 320 /* If the cache is disabled, just generate the content */
318 if (size <= 0) { 321 if (size <= 0) {
319 fn(cbdata); 322 fn(cbdata);
320 return 0; 323 return 0;
321 } 324 }
322 325
323 /* Verify input, calculate filenames */ 326 /* Verify input, calculate filenames */
324 if (!path) { 327 if (!path) {
325 cache_log("[cgit] Cache path not specified, caching is disabled\n"); 328 cache_log("[cgit] Cache path not specified, caching is disabled\n");
326 fn(cbdata); 329 fn(cbdata);
327 return 0; 330 return 0;
328 } 331 }
329 len = strlen(path); 332 len = strlen(path);
330 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ 333 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */
331 cache_log("[cgit] Cache path too long, caching is disabled: %s\n", 334 cache_log("[cgit] Cache path too long, caching is disabled: %s\n",
332 path); 335 path);
333 fn(cbdata); 336 fn(cbdata);
334 return 0; 337 return 0;
335 } 338 }
336 if (!key) 339 if (!key)
337 key = ""; 340 key = "";
338 hash = hash_str(key) % size; 341 hash = hash_str(key) % size;
339 strcpy(filename, path); 342 strcpy(filename, path);
340 if (filename[len - 1] != '/') 343 if (filename[len - 1] != '/')
341 filename[len++] = '/'; 344 filename[len++] = '/';
342 for(i = 0; i < 8; i++) { 345 for(i = 0; i < 8; i++) {
343 sprintf(filename + len++, "%x", 346 sprintf(filename + len++, "%x",
344 (unsigned char)(hash & 0xf)); 347 (unsigned char)(hash & 0xf));
345 hash >>= 4; 348 hash >>= 4;
346 } 349 }
347 filename[len] = '\0'; 350 filename[len] = '\0';
348 strcpy(lockname, filename); 351 strcpy(lockname, filename);
349 strcpy(lockname + len, ".lock"); 352 strcpy(lockname + len, ".lock");
350 slot.fn = fn; 353 slot.fn = fn;
351 slot.cbdata = cbdata; 354 slot.cbdata = cbdata;
352 slot.ttl = ttl; 355 slot.ttl = ttl;
353 slot.cache_name = filename; 356 slot.cache_name = filename;
354 slot.lock_name = lockname; 357 slot.lock_name = lockname;
355 slot.key = key; 358 slot.key = key;
356 slot.keylen = strlen(key); 359 slot.keylen = strlen(key);
357 return process_slot(&slot); 360 return process_slot(&slot);
358} 361}
359 362
360/* Return a strftime formatted date/time 363/* Return a strftime formatted date/time
361 * NB: the result from this function is to shared memory 364 * NB: the result from this function is to shared memory
362 */ 365 */
363char *sprintftime(const char *format, time_t time) 366char *sprintftime(const char *format, time_t time)
364{ 367{
365 static char buf[64]; 368 static char buf[64];
366 struct tm *tm; 369 struct tm *tm;
367 370
368 if (!time) 371 if (!time)
369 return NULL; 372 return NULL;
370 tm = gmtime(&time); 373 tm = gmtime(&time);
371 strftime(buf, sizeof(buf)-1, format, tm); 374 strftime(buf, sizeof(buf)-1, format, tm);
372 return buf; 375 return buf;
373} 376}
374 377
375int cache_ls(const char *path) 378int cache_ls(const char *path)
376{ 379{
377 DIR *dir; 380 DIR *dir;
378 struct dirent *ent; 381 struct dirent *ent;
379 int err = 0; 382 int err = 0;
380 struct cache_slot slot; 383 struct cache_slot slot;
381 char fullname[1024]; 384 char fullname[1024];
382 char *name; 385 char *name;
383 386
384 if (!path) { 387 if (!path) {
385 cache_log("[cgit] cache path not specified\n"); 388 cache_log("[cgit] cache path not specified\n");
386 return -1; 389 return -1;
387 } 390 }
388 if (strlen(path) > 1024 - 10) { 391 if (strlen(path) > 1024 - 10) {
389 cache_log("[cgit] cache path too long: %s\n", 392 cache_log("[cgit] cache path too long: %s\n",
390 path); 393 path);
391 return -1; 394 return -1;
392 } 395 }
393 dir = opendir(path); 396 dir = opendir(path);
394 if (!dir) { 397 if (!dir) {
395 err = errno; 398 err = errno;
396 cache_log("[cgit] unable to open path %s: %s (%d)\n", 399 cache_log("[cgit] unable to open path %s: %s (%d)\n",
397 path, strerror(err), err); 400 path, strerror(err), err);
398 return err; 401 return err;
399 } 402 }
400 strcpy(fullname, path); 403 strcpy(fullname, path);
401 name = fullname + strlen(path); 404 name = fullname + strlen(path);
402 if (*(name - 1) != '/') { 405 if (*(name - 1) != '/') {
403 *name++ = '/'; 406 *name++ = '/';
404 *name = '\0'; 407 *name = '\0';
405 } 408 }
406 slot.cache_name = fullname; 409 slot.cache_name = fullname;
407 while((ent = readdir(dir)) != NULL) { 410 while((ent = readdir(dir)) != NULL) {
408 if (strlen(ent->d_name) != 8) 411 if (strlen(ent->d_name) != 8)
409 continue; 412 continue;
410 strcpy(name, ent->d_name); 413 strcpy(name, ent->d_name);
411 if ((err = open_slot(&slot)) != 0) { 414 if ((err = open_slot(&slot)) != 0) {
412 cache_log("[cgit] unable to open path %s: %s (%d)\n", 415 cache_log("[cgit] unable to open path %s: %s (%d)\n",
413 fullname, strerror(err), err); 416 fullname, strerror(err), err);
414 continue; 417 continue;
415 } 418 }
416 printf("%s %s %10lld %s\n", 419 printf("%s %s %10lld %s\n",
417 name, 420 name,
418 sprintftime("%Y-%m-%d %H:%M:%S", 421 sprintftime("%Y-%m-%d %H:%M:%S",
419 slot.cache_st.st_mtime), 422 slot.cache_st.st_mtime),
420 slot.cache_st.st_size, 423 slot.cache_st.st_size,
421 slot.buf); 424 slot.buf);
422 close_slot(&slot); 425 close_slot(&slot);
423 } 426 }
424 closedir(dir); 427 closedir(dir);
425 return 0; 428 return 0;
426} 429}
427 430
428/* Print a message to stdout */ 431/* Print a message to stdout */
429void cache_log(const char *format, ...) 432void cache_log(const char *format, ...)
430{ 433{
431 va_list args; 434 va_list args;
432 va_start(args, format); 435 va_start(args, format);
433 vfprintf(stderr, format, args); 436 vfprintf(stderr, format, args);
434 va_end(args); 437 va_end(args);
435} 438}
436 439