Mercurial > notdcc
comparison dcclib/mkstemp.c @ 0:c7f6b056b673
First import of vendor version
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 13:49:58 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c7f6b056b673 |
---|---|
1 /* Distributed Checksum Clearinghouse | |
2 * | |
3 * Copyright (c) 2008 by Rhyolite Software, LLC | |
4 * | |
5 * This agreement is not applicable to any entity which sells anti-spam | |
6 * solutions to others or provides an anti-spam solution as part of a | |
7 * security solution sold to other entities, or to a private network | |
8 * which employs the DCC or uses data provided by operation of the DCC | |
9 * but does not provide corresponding data to other users. | |
10 * | |
11 * Permission to use, copy, modify, and distribute this software without | |
12 * changes for any purpose with or without fee is hereby granted, provided | |
13 * that the above copyright notice and this permission notice appear in all | |
14 * copies and any distributed versions or copies are either unchanged | |
15 * or not called anything similar to "DCC" or "Distributed Checksum | |
16 * Clearinghouse". | |
17 * | |
18 * Parties not eligible to receive a license under this agreement can | |
19 * obtain a commercial license to use DCC by contacting Rhyolite Software | |
20 * at sales@rhyolite.com. | |
21 * | |
22 * A commercial license would be for Distributed Checksum and Reputation | |
23 * Clearinghouse software. That software includes additional features. This | |
24 * free license for Distributed ChecksumClearinghouse Software does not in any | |
25 * way grant permision to use Distributed Checksum and Reputation Clearinghouse | |
26 * software | |
27 * | |
28 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL | |
29 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | |
30 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC | |
31 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES | |
32 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
33 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
34 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
35 * SOFTWARE. | |
36 * | |
37 * Rhyolite Software DCC 1.3.103-1.54 $Revision$ | |
38 */ | |
39 | |
40 #include "dcc_defs.h" | |
41 #include "dcc_paths.h" | |
42 #ifdef DCC_WIN32 | |
43 #include <direct.h> | |
44 #else | |
45 #include <dirent.h> | |
46 #endif | |
47 | |
48 static struct { | |
49 DCC_PATH path; /* has trailing '/' */ | |
50 } tmp[3]; | |
51 static int num_tmp_paths; | |
52 | |
53 static u_int32_t gen; | |
54 | |
55 | |
56 static const char * | |
57 mkstr(char str[DCC_MKSTEMP_LEN+1]) | |
58 { | |
59 static const char digits[] = "0123456789" | |
60 #ifndef DCC_WIN32 | |
61 "abcdefghijklmnopqrstuvwxyz" | |
62 #endif | |
63 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
64 u_int32_t n; | |
65 int i; | |
66 | |
67 /* (re)start the generator and take a number | |
68 * There is a race among threads here to compute ++gen. | |
69 * However, it is rare and the worst that happens is that one thread | |
70 * will lose when it tries to create the file, just as it might lose | |
71 * to another process, and so need to come here again. */ | |
72 if (gen == 0) | |
73 gen = (getpid() << 16) + time(0); | |
74 n = ++gen; | |
75 | |
76 i = DCC_MKSTEMP_LEN; | |
77 str[i] = '\0'; | |
78 do { | |
79 str[--i] = digits[n % (LITZ(digits))]; | |
80 n /= (LITZ(digits)); | |
81 } while (i > 0); | |
82 return str; | |
83 } | |
84 | |
85 | |
86 | |
87 /* Some platforms have broken implementations of mkstemp() that generate | |
88 * only 32 different names. | |
89 * Given the main uses for mkstemp() in the DCC for log files in directories | |
90 * used only by the DCC, it is nice to try to make the names sort of | |
91 * sequential for a given dccm process. That means nothing more than putting | |
92 * the random bits in the most significant bits of the seed and using | |
93 * a small constant for the addition in the random number generator that is | |
94 * commonly used, and remembering the generator. */ | |
95 int /* -1 or FD of temporary file */ | |
96 dcc_mkstemp(DCC_EMSG emsg, | |
97 char *nm, int nm_len, /* put the generated name here */ | |
98 char *id, int id_len, /* put unique name string here */ | |
99 const char *old, /* try this name first */ | |
100 const char *tgtdir, /* directory or 0 for tmp_path */ | |
101 const char *p2, /* rest of the name */ | |
102 u_char close_del, /* 1=delete on close */ | |
103 int mode) /* add these mode bits to 0600 */ | |
104 { | |
105 char str[DCC_MKSTEMP_LEN+1]; | |
106 int fd, limit, serrno; | |
107 const char *p1; | |
108 int i; | |
109 #ifdef DCC_WIN32 | |
110 HANDLE h; | |
111 DWORD flags; | |
112 #else | |
113 mode_t old_mask; | |
114 #endif | |
115 | |
116 if (!p2) | |
117 p2 = ""; | |
118 p1 = tgtdir; | |
119 if (!p1) { | |
120 if (!num_tmp_paths) | |
121 tmp_path_init(0, 0); | |
122 p1 = tmp[0].path; | |
123 } | |
124 | |
125 /* this loop should almost always need only a single pass */ | |
126 limit = 0; | |
127 for (;;) { | |
128 if (*p2 == '/') { /* avoid "//" */ | |
129 i = strlen(p1); | |
130 if (i > 0 && p1[i-1] == '/') | |
131 ++p2; | |
132 } | |
133 | |
134 if (old) { | |
135 i = snprintf(nm, nm_len, | |
136 "%s%s%."DCC_MKSTEMP_LEN_STR"s", | |
137 p1, p2, old); | |
138 } else { | |
139 i = snprintf(nm, nm_len, | |
140 "%s%s%s", | |
141 p1, p2, mkstr(str)); | |
142 } | |
143 if (i >= nm_len) { | |
144 dcc_pemsg(EX_SOFTWARE, emsg, | |
145 "temporary file name \"%s\" too big", nm); | |
146 *nm = '\0'; | |
147 return -1; | |
148 } | |
149 | |
150 #ifdef DCC_WIN32 | |
151 /* Given the uses of this function, if the temporary | |
152 * file is open, then it has been abandoned. All valid | |
153 * uses have the file renamed. Windows does not allow | |
154 * open files to be unlinked. FILE_FLAGS_DELETE_ON_CLOSE | |
155 * does not seem to be supported everywhere or it is | |
156 * unreliable. So open existing files without sharing | |
157 * but truncated. | |
158 * Use CreateFile() because the Borland and Microsoft | |
159 * open(), _open(), and _sopen() functions differ */ | |
160 flags = (close_del | |
161 ? FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | |
162 : FILE_ATTRIBUTE_NORMAL); | |
163 h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0, | |
164 0, CREATE_NEW, flags, 0); | |
165 if (h == INVALID_HANDLE_VALUE) | |
166 h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0, | |
167 0, TRUNCATE_EXISTING, flags, 0); | |
168 if (h != INVALID_HANDLE_VALUE) { | |
169 fd = _open_osfhandle((long)h, 0); | |
170 if (fd >= 0) { | |
171 if (id && *id == '\0') | |
172 STRLCPY(id, str, id_len); | |
173 return fd; | |
174 } | |
175 dcc_pemsg(EX_IOERR, emsg, "_open_osfhandle(%s): %s", | |
176 nm, ERROR_STR()); | |
177 CloseHandle(h); | |
178 *nm = '\0'; | |
179 return -1; | |
180 } | |
181 serrno = errno; | |
182 #else | |
183 old_mask = umask(02); | |
184 fd = open(nm, O_RDWR | O_CREAT | O_EXCL, (mode & 0777) | 0600); | |
185 serrno = errno; | |
186 umask(old_mask); | |
187 if (fd >= 0) { | |
188 if (close_del | |
189 && 0 > unlink(nm)) { | |
190 dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s", | |
191 nm, ERROR_STR1(serrno)); | |
192 close(fd); | |
193 *nm = '\0'; | |
194 return -1; | |
195 } | |
196 if (id && *id == '\0') | |
197 STRLCPY(id, str, id_len); | |
198 return fd; | |
199 } | |
200 #endif | |
201 | |
202 if (errno != EEXIST) { | |
203 if (tgtdir != 0 || p1 == tmp[num_tmp_paths-1].path) { | |
204 dcc_pemsg(EX_IOERR, emsg, "open(%s): %s", | |
205 nm, ERROR_STR1(serrno)); | |
206 *nm = '\0'; | |
207 return -1; | |
208 } | |
209 p1 += sizeof(tmp[num_tmp_paths].path); | |
210 continue; | |
211 } | |
212 if (++limit > 100) { | |
213 dcc_pemsg(EX_IOERR, emsg, "open(%s) %d times: %s", | |
214 nm, limit, ERROR_STR1(EEXIST)); | |
215 *nm = '\0'; | |
216 return -1; | |
217 } | |
218 | |
219 if (old) { | |
220 old = 0; | |
221 } else { | |
222 /* There is already a file of the generated name. | |
223 * That implies that the current sequence of names | |
224 * has collided with an older sequence. | |
225 * So start another sequence. */ | |
226 gen = 0; | |
227 } | |
228 } | |
229 } | |
230 | |
231 | |
232 | |
233 | |
234 DCC_PATH dcc_main_logdir; | |
235 static LOG_MODE dcc_main_log_mode; | |
236 | |
237 /* see if a directory is a suitable for DCC client log files */ | |
238 static int /* 1=ok, -2=non-existent, -1=bad */ | |
239 stat_logdir(DCC_EMSG emsg, | |
240 const char *logdir, | |
241 struct stat *sb) | |
242 { | |
243 int result; | |
244 | |
245 if (0 > stat(logdir, sb)) { | |
246 if (errno == ENOTDIR || errno == ENOENT) { | |
247 result = -2; | |
248 } else { | |
249 result = -1; | |
250 } | |
251 dcc_pemsg(EX_IOERR, emsg, "stat(log directory \"%s\"): %s", | |
252 logdir, ERROR_STR()); | |
253 return result; | |
254 } | |
255 | |
256 if (!S_ISDIR(sb->st_mode)) { | |
257 dcc_pemsg(EX_IOERR, emsg, "\"%s\" is not a log directory", | |
258 logdir); | |
259 return -1; | |
260 } | |
261 return 1; | |
262 } | |
263 | |
264 | |
265 | |
266 static void | |
267 tmpdir_ck(const char *dir, u_char complain) | |
268 { | |
269 #ifndef DCC_WIN32 | |
270 struct stat sb; | |
271 #endif | |
272 char *path; | |
273 | |
274 if (!dir || *dir == '\0') { | |
275 if (complain) | |
276 dcc_error_msg("null -T temporary directory"); | |
277 return; | |
278 } | |
279 | |
280 path = tmp[num_tmp_paths].path; | |
281 if (!fnm2abs(path, dir, "") | |
282 || strlen(path) > ISZ(DCC_PATH)-DCC_MKSTEMP_LEN-2) { | |
283 if (complain) | |
284 dcc_error_msg("-T \"%s\" too long", dir); | |
285 path[0] = '\0'; | |
286 return; | |
287 } | |
288 ++num_tmp_paths; | |
289 | |
290 #ifndef DCC_WIN32 /* stat(directory) fails on Windows XP */ | |
291 if (complain) { | |
292 if (0 > stat(path, &sb)) { | |
293 dcc_error_msg("stat(temporary directory \"%s\"): %s", | |
294 path, ERROR_STR()); | |
295 return; | |
296 } | |
297 if (!S_ISDIR(sb.st_mode)) { | |
298 dcc_error_msg("\"%s\" is not a temporary directory", | |
299 path); | |
300 return; | |
301 } | |
302 #ifdef HAVE_EACCESS | |
303 if (0 > eaccess(path, W_OK)) { | |
304 dcc_error_msg("temporary directory \"%s\": %s", | |
305 path, ERROR_STR()); | |
306 return; | |
307 } | |
308 #endif | |
309 } | |
310 #endif /* !DCC_WIN32 */ | |
311 | |
312 strcat(path, "/"); | |
313 } | |
314 | |
315 | |
316 | |
317 /* get the temporary directory ready */ | |
318 void | |
319 tmp_path_init(const char *dir1, /* prefer this & complain if bad */ | |
320 const char *dir2) /* then this but no complaint */ | |
321 { | |
322 if (dir1) | |
323 tmpdir_ck(dir1, 1); | |
324 | |
325 if (dir2) | |
326 tmpdir_ck(dir2, 0); | |
327 | |
328 tmpdir_ck(_PATH_TMP, 1); | |
329 } | |
330 | |
331 | |
332 /* Initialize the main DCC client log directory including parsing the | |
333 * prefix of subdirectory type. In case the path is relative, | |
334 * this should be called after the change to the DCC home directory */ | |
335 u_char /* 0=failed 1=ok */ | |
336 dcc_main_logdir_init(DCC_EMSG emsg, const char *arg) | |
337 { | |
338 struct stat sb; | |
339 | |
340 if (!arg) | |
341 return 1; | |
342 | |
343 /* if the directory name starts with "D?", "H?", or "M?", | |
344 * automatically add subdirectories */ | |
345 if (!CLITCMP(arg, "D?")) { | |
346 dcc_main_log_mode = LOG_MODE_DAY; | |
347 arg += LITZ("D?"); | |
348 | |
349 } else if (!CLITCMP(arg, "H?")) { | |
350 dcc_main_log_mode = LOG_MODE_HOUR; | |
351 arg += LITZ("H?"); | |
352 | |
353 } else if (!CLITCMP(arg, "M?")) { | |
354 dcc_main_log_mode = LOG_MODE_MINUTE; | |
355 arg += LITZ("M?"); | |
356 | |
357 } else { | |
358 dcc_main_log_mode = LOG_MODE_FLAT; | |
359 } | |
360 | |
361 if (!fnm2rel(dcc_main_logdir, arg, 0)) { | |
362 dcc_pemsg(EX_DATAERR, emsg, "bad log directry \"%s\"", arg); | |
363 return 0; | |
364 } | |
365 | |
366 return stat_logdir(emsg, dcc_main_logdir, &sb) > 0; | |
367 } | |
368 | |
369 | |
370 | |
371 static u_char | |
372 mklogsubdir(DCC_EMSG emsg, | |
373 DCC_PATH dir, /* put directory name here */ | |
374 struct stat *dir_sb, | |
375 const char *grandparent, | |
376 const char *parent, | |
377 const struct stat *parent_sb, | |
378 const char *pat, u_int dirnum) | |
379 { | |
380 int stat_result; | |
381 int i; | |
382 | |
383 i = snprintf(dir, sizeof(DCC_PATH), pat, parent, dirnum); | |
384 if (i >= ISZ(DCC_PATH)) { | |
385 dcc_pemsg(EX_DATAERR, emsg, "long log directry name \"%s\"", | |
386 grandparent); | |
387 return 0; | |
388 } | |
389 | |
390 /* see if it already exists */ | |
391 stat_result = stat_logdir(emsg, dir, dir_sb); | |
392 if (stat_result == -1) | |
393 return 0; | |
394 | |
395 #ifdef DCC_WIN32 | |
396 if (stat_result == -2 && 0 > mkdir(dir)) { | |
397 dcc_pemsg(EX_IOERR, emsg, "mkdir(%s): %s", | |
398 dir, ERROR_STR()); | |
399 return 0; | |
400 } | |
401 return 1; | |
402 #else | |
403 if (stat_result == -2) { | |
404 mode_t old_mask; | |
405 | |
406 old_mask = umask(02); | |
407 if (0 > mkdir(dir, (parent_sb->st_mode & 0775) | 0700)) { | |
408 dcc_pemsg(EX_IOERR, emsg, "mkdir(%s): %s", | |
409 dir, ERROR_STR()); | |
410 umask(old_mask); | |
411 return 0; | |
412 } | |
413 umask(old_mask); | |
414 stat_result = stat_logdir(emsg, dir, dir_sb); | |
415 if (stat_result < 0) | |
416 return 0; | |
417 } | |
418 /* set GID if necessary */ | |
419 if (dir_sb->st_gid != parent_sb->st_gid) { | |
420 if (0 > chown(dir, dir_sb->st_uid, parent_sb->st_gid)) { | |
421 /* this is expected to be needed and to work | |
422 * only for root */ | |
423 if (errno == EPERM) | |
424 return 1; | |
425 dcc_pemsg(EX_IOERR, emsg, "chown(%s,%d,%d): %s", | |
426 dir, (int)dir_sb->st_uid, | |
427 (int)parent_sb->st_gid, | |
428 ERROR_STR()); | |
429 return 0; | |
430 } | |
431 dir_sb->st_gid = parent_sb->st_gid; | |
432 } | |
433 return 1; | |
434 #endif /* DCC_WIN32 */ | |
435 } | |
436 | |
437 | |
438 | |
439 /* create and open a DCC client log file */ | |
440 int /* fd -1=no directory -2=serious */ | |
441 dcc_log_open(DCC_EMSG emsg, | |
442 DCC_PATH log_path, /* put the log file name here */ | |
443 char *id, int id_len, /* put log ID string here */ | |
444 const char *old, /* try this name first */ | |
445 const char *logdir, | |
446 const char *prefix, /* DCC_*_LOG_PREFIX */ | |
447 LOG_MODE log_mode) | |
448 { | |
449 time_t now; | |
450 struct tm tm; | |
451 DCC_PATH dir_a, dir_b; | |
452 struct stat sb_a, sb_b, sb_f; | |
453 const struct stat *sb; | |
454 int stat_result, fd; | |
455 | |
456 log_path[0] = '\0'; | |
457 | |
458 stat_result = stat_logdir(emsg, logdir, &sb_b); | |
459 if (stat_result < 0) | |
460 return stat_result; | |
461 | |
462 if (log_mode == LOG_MODE_FLAT) { | |
463 sb = &sb_b; | |
464 | |
465 } else { | |
466 now = time(0); | |
467 dcc_localtime(now, &tm); | |
468 | |
469 if (!mklogsubdir(emsg, dir_a, &sb_a, | |
470 logdir, logdir, &sb_b, | |
471 "%s/%03d", tm.tm_yday+1)) | |
472 return -2; | |
473 | |
474 if (log_mode == LOG_MODE_DAY) { | |
475 logdir = dir_a; | |
476 sb = &sb_a; | |
477 } else { | |
478 if (!mklogsubdir(emsg, dir_b, &sb_b, | |
479 logdir, dir_a, &sb_a, | |
480 "%s/%02d", tm.tm_hour)) | |
481 return -2; | |
482 | |
483 if (log_mode == LOG_MODE_HOUR) { | |
484 logdir = dir_b; | |
485 sb = &sb_b; | |
486 } else { | |
487 if (!mklogsubdir(emsg, dir_a, &sb_a, | |
488 logdir, dir_b, &sb_b, | |
489 "%s/%02d", tm.tm_min)) | |
490 return -2; | |
491 logdir = dir_a; | |
492 sb = &sb_a; | |
493 } | |
494 } | |
495 } | |
496 | |
497 fd = dcc_mkstemp(emsg, log_path, sizeof(DCC_PATH), id, id_len, old, | |
498 logdir, prefix, 0, sb->st_mode & 0640); | |
499 if (fd < 0) | |
500 return -2; | |
501 | |
502 /* give it the right GID */ | |
503 if (0 > fstat(fd, &sb_f)) { | |
504 dcc_pemsg(EX_IOERR, emsg, "fstat(%s): %s", | |
505 log_path, ERROR_STR()); | |
506 close(fd); | |
507 return -2; | |
508 } | |
509 #ifndef DCC_WIN32 | |
510 if (sb->st_gid != sb_f.st_gid | |
511 && 0 > fchown(fd, sb_f.st_uid, sb->st_gid) | |
512 && errno != EPERM) { | |
513 /* this is expected to be needed and to work only for root */ | |
514 dcc_pemsg(EX_IOERR, emsg, "chown(%s,%d,%d): %s", | |
515 log_path, (int)sb_f.st_uid, (int)sb->st_gid, | |
516 ERROR_STR()); | |
517 close(fd); | |
518 return -2; | |
519 } | |
520 #endif | |
521 return fd; | |
522 } | |
523 | |
524 | |
525 | |
526 /* create and open a main DCC client log file */ | |
527 int /* fd -1=no directory -2=serious */ | |
528 dcc_main_log_open(DCC_EMSG emsg, | |
529 DCC_PATH log_path, /* put the log file name here */ | |
530 char *id, int id_len) /* put log ID string here */ | |
531 { | |
532 return dcc_log_open(emsg, log_path, id, id_len, 0, | |
533 dcc_main_logdir, DCC_TMP_LOG_PREFIX, | |
534 dcc_main_log_mode); | |
535 } | |
536 | |
537 | |
538 /* rename a DCC client log file to a unique name */ | |
539 u_char | |
540 dcc_log_keep(DCC_EMSG emsg, DCC_PATH cur_path) | |
541 { | |
542 DCC_PATH new_path; | |
543 char str[DCC_MKSTEMP_LEN+1]; | |
544 int path_len; | |
545 int limit; | |
546 | |
547 path_len = strlen(cur_path); | |
548 if (path_len < LITZ(DCC_TMP_LOG_PREFIX)+DCC_MKSTEMP_LEN) | |
549 dcc_logbad(EX_SOFTWARE, | |
550 "dcc_log_keep(%s): path too short", cur_path); | |
551 if (path_len >= ISZ(new_path)) | |
552 dcc_logbad(EX_SOFTWARE, | |
553 "dcc_log_keep(%s): path too long", cur_path); | |
554 | |
555 /* start by trying the current name with "tmp" replaced by "msg" */ | |
556 memcpy(new_path, cur_path, path_len+1); | |
557 memcpy(new_path+(path_len - (LITZ(DCC_TMP_LOG_PREFIX)+DCC_MKSTEMP_LEN)), | |
558 DCC_FIN_LOG_PREFIX, LITZ(DCC_FIN_LOG_PREFIX)); | |
559 limit = 0; | |
560 for (;;) { | |
561 #ifdef DCC_WIN32 | |
562 /* Windows does not have hard links */ | |
563 if (!rename(cur_path, new_path)) { | |
564 memcpy(cur_path, new_path, path_len); | |
565 return 1; | |
566 } | |
567 if (limit > 100 || errno != EACCES) { | |
568 dcc_pemsg(EX_IOERR, emsg, "rename(%s,%s) #%d: %s", | |
569 cur_path, new_path, limit, ERROR_STR()); | |
570 return 0; | |
571 } | |
572 #else | |
573 /* rename() on some UNIX systems deletes a pre-existing | |
574 * target, so we must use link() and unlink() */ | |
575 if (link(cur_path, new_path) >= 0) { | |
576 /* we made the link, so unlink the old name */ | |
577 if (unlink(cur_path) < 0) { | |
578 dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s", | |
579 cur_path, ERROR_STR()); | |
580 return 0; | |
581 } | |
582 memcpy(cur_path, new_path, path_len); | |
583 return 1; | |
584 } | |
585 if (limit > 100 || errno != EEXIST) { | |
586 dcc_pemsg(EX_IOERR, emsg, "link(%s,%s): %s", | |
587 cur_path, new_path, ERROR_STR()); | |
588 return 0; | |
589 } | |
590 #endif | |
591 | |
592 /* look for another sequence of names | |
593 * if our new choice for a name was taken */ | |
594 if (++limit > 1) | |
595 gen = 0; | |
596 memcpy(new_path+path_len-DCC_MKSTEMP_LEN, | |
597 mkstr(str), DCC_MKSTEMP_LEN); | |
598 } | |
599 } | |
600 | |
601 | |
602 | |
603 u_char | |
604 dcc_log_close(DCC_EMSG emsg, const char *nm, int fd, | |
605 const struct timeval *ldate) | |
606 { | |
607 DCC_PATH path; | |
608 u_char result; | |
609 | |
610 result = dcc_set_mtime(emsg, nm, fd, ldate); | |
611 if (0 > close(fd) | |
612 && result) { | |
613 dcc_pemsg(EX_IOERR, emsg,"close(%s): %s", | |
614 fnm2abs_err(path, nm), ERROR_STR()); | |
615 result = 0; | |
616 } | |
617 return result; | |
618 } |