0
|
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 } |