Commit | Line | Data |
---|---|---|
0756eb3c PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
1e1ddfac | 6 | /* Copyright (c) The Exim Maintainers 2020 */ |
0756eb3c PH |
7 | /* See the file NOTICE for conditions of use and distribution. */ |
8 | ||
9 | /* Functions in support of the use of maildirsize files for handling quotas in | |
10 | maildir directories. Some of the rules are a bit baroque: | |
11 | ||
12 | http://www.inter7.com/courierimap/README.maildirquota.html | |
13 | ||
14 | We try to follow most of that, except that the directories to skip for quota | |
15 | calculations are not hard wired in, but are supplied as a regex. */ | |
16 | ||
17 | ||
18 | #include "../exim.h" | |
19 | #include "appendfile.h" | |
20 | #include "tf_maildir.h" | |
21 | ||
22 | #define MAX_FILE_SIZE 5120 | |
23 | ||
24 | ||
25 | ||
26 | /************************************************* | |
27 | * Ensure maildir directories exist * | |
28 | *************************************************/ | |
29 | ||
30 | /* This function is called at the start of a maildir delivery, to ensure that | |
d6629cdc PH |
31 | all the relevant directories exist. It also creates a maildirfolder file if the |
32 | base directory matches a given pattern. | |
0756eb3c PH |
33 | |
34 | Argument: | |
35 | path the base directory name | |
36 | addr the address item (for setting an error message) | |
37 | create_directory true if we are allowed to create missing directories | |
38 | dirmode the mode for created directories | |
d6629cdc PH |
39 | maildirfolder_create_regex |
40 | the pattern to match for maildirfolder creation | |
0756eb3c PH |
41 | |
42 | Returns: TRUE on success; FALSE on failure | |
43 | */ | |
44 | ||
45 | BOOL maildir_ensure_directories(uschar *path, address_item *addr, | |
d6629cdc | 46 | BOOL create_directory, int dirmode, uschar *maildirfolder_create_regex) |
0756eb3c PH |
47 | { |
48 | int i; | |
49 | struct stat statbuf; | |
1ba28e2b | 50 | const char *subdirs[] = { "/tmp", "/new", "/cur" }; |
0756eb3c PH |
51 | |
52 | DEBUG(D_transport) | |
53 | debug_printf("ensuring maildir directories exist in %s\n", path); | |
54 | ||
55 | /* First ensure that the path we have is a directory; if it does not exist, | |
56 | create it. Then make sure the tmp, new & cur subdirs of the maildir are | |
d6629cdc | 57 | there. If not, fail. This aborts the delivery (even though the cur subdir is |
0756eb3c PH |
58 | not actually needed for delivery). Handle all 4 directory tests/creates in a |
59 | loop so that code can be shared. */ | |
60 | ||
61 | for (i = 0; i < 4; i++) | |
62 | { | |
63 | int j; | |
1ba28e2b | 64 | const uschar *dir, *mdir; |
0756eb3c PH |
65 | |
66 | if (i == 0) | |
67 | { | |
1ba28e2b | 68 | mdir = CUS""; |
0756eb3c PH |
69 | dir = path; |
70 | } | |
71 | else | |
72 | { | |
1ba28e2b | 73 | mdir = CUS subdirs[i-1]; |
0756eb3c PH |
74 | dir = mdir + 1; |
75 | } | |
76 | ||
77 | /* Check an existing path is a directory. This is inside a loop because | |
78 | there is a potential race condition when creating the directory - some | |
79 | other process may get there first. Give up after trying several times, | |
80 | though. */ | |
81 | ||
82 | for (j = 0; j < 10; j++) | |
83 | { | |
84 | if (Ustat(dir, &statbuf) == 0) | |
85 | { | |
86 | if (S_ISDIR(statbuf.st_mode)) break; /* out of the race loop */ | |
87 | addr->message = string_sprintf("%s%s is not a directory", path, | |
88 | mdir); | |
89 | addr->basic_errno = ERRNO_NOTDIRECTORY; | |
90 | return FALSE; | |
91 | } | |
92 | ||
93 | /* Try to make if non-existent and configured to do so */ | |
94 | ||
95 | if (errno == ENOENT && create_directory) | |
96 | { | |
97 | if (!directory_make(NULL, dir, dirmode, FALSE)) | |
98 | { | |
99 | if (errno == EEXIST) continue; /* repeat the race loop */ | |
100 | addr->message = string_sprintf("cannot create %s%s", path, mdir); | |
101 | addr->basic_errno = errno; | |
102 | return FALSE; | |
103 | } | |
104 | DEBUG(D_transport) | |
105 | debug_printf("created directory %s%s\n", path, mdir); | |
106 | break; /* out of the race loop */ | |
107 | } | |
108 | ||
109 | /* stat() error other than ENOENT, or ENOENT and not creatable */ | |
110 | ||
111 | addr->message = string_sprintf("stat() error for %s%s: %s", path, mdir, | |
112 | strerror(errno)); | |
113 | addr->basic_errno = errno; | |
114 | return FALSE; | |
115 | } | |
116 | ||
117 | /* If we went round the loop 10 times, the directory was flickering in | |
118 | and out of existence like someone in a malfunctioning Star Trek | |
119 | transporter. */ | |
120 | ||
121 | if (j >= 10) | |
122 | { | |
123 | addr->message = string_sprintf("existence of %s%s unclear\n", path, | |
124 | mdir); | |
125 | addr->basic_errno = errno; | |
126 | addr->special_action = SPECIAL_FREEZE; | |
127 | return FALSE; | |
128 | } | |
129 | ||
130 | /* First time through the directories loop, cd to the main directory */ | |
131 | ||
132 | if (i == 0 && Uchdir(path) != 0) | |
133 | { | |
134 | addr->message = string_sprintf ("cannot chdir to %s", path); | |
135 | addr->basic_errno = errno; | |
136 | return FALSE; | |
137 | } | |
138 | } | |
139 | ||
d6629cdc PH |
140 | /* If the basic path matches maildirfolder_create_regex, we are dealing with |
141 | a subfolder, and should ensure that a maildirfolder file exists. */ | |
142 | ||
143 | if (maildirfolder_create_regex != NULL) | |
144 | { | |
145 | const uschar *error; | |
146 | int offset; | |
147 | const pcre *regex; | |
148 | ||
149 | DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n"); | |
150 | ||
3d2e82c5 JH |
151 | if (!(regex = pcre_compile(CS maildirfolder_create_regex, PCRE_COPT, |
152 | CCSS &error, &offset, NULL))) | |
d6629cdc PH |
153 | { |
154 | addr->message = string_sprintf("appendfile: regular expression " | |
155 | "error: %s at offset %d while compiling %s", error, offset, | |
156 | maildirfolder_create_regex); | |
157 | return FALSE; | |
158 | } | |
159 | ||
160 | if (pcre_exec(regex, NULL, CS path, Ustrlen(path), 0, 0, NULL, 0) >= 0) | |
161 | { | |
162 | uschar *fname = string_sprintf("%s/maildirfolder", path); | |
163 | if (Ustat(fname, &statbuf) == 0) | |
164 | { | |
165 | DEBUG(D_transport) debug_printf("maildirfolder already exists\n"); | |
166 | } | |
167 | else | |
168 | { | |
5f28a6e8 | 169 | int fd = Uopen(fname, O_WRONLY|O_APPEND|O_CREAT, 0600); |
d6629cdc PH |
170 | if (fd < 0) |
171 | { | |
172 | addr->message = string_sprintf("appendfile: failed to create " | |
173 | "maildirfolder file in %s directory: %s", path, strerror(errno)); | |
174 | return FALSE; | |
175 | } | |
176 | (void)close(fd); | |
177 | DEBUG(D_transport) debug_printf("created maildirfolder file\n"); | |
178 | } | |
179 | } | |
180 | else | |
181 | { | |
182 | DEBUG(D_transport) debug_printf("maildirfolder file not required\n"); | |
183 | } | |
184 | } | |
185 | ||
186 | return TRUE; /* Everything exists that should exist */ | |
0756eb3c PH |
187 | } |
188 | ||
189 | ||
190 | ||
191 | ||
192 | /************************************************* | |
193 | * Update maildirsizefile for new file * | |
194 | *************************************************/ | |
195 | ||
196 | /* This function is called to add a new line to the file, recording the length | |
197 | of the newly added message. There isn't much we can do on failure... | |
198 | ||
199 | Arguments: | |
200 | fd the open file descriptor | |
201 | size the size of the message | |
202 | ||
203 | Returns: nothing | |
204 | */ | |
205 | ||
206 | void | |
207 | maildir_record_length(int fd, int size) | |
208 | { | |
209 | int len; | |
210 | uschar buffer[256]; | |
211 | sprintf(CS buffer, "%d 1\n", size); | |
212 | len = Ustrlen(buffer); | |
d315eda1 JH |
213 | if (lseek(fd, 0, SEEK_END) >= 0) |
214 | { | |
215 | len = write(fd, buffer, len); | |
216 | DEBUG(D_transport) | |
217 | debug_printf("added '%.*s' to maildirsize file\n", len-1, buffer); | |
218 | } | |
0756eb3c PH |
219 | } |
220 | ||
221 | ||
222 | ||
223 | /************************************************* | |
224 | * Find the size of a maildir * | |
225 | *************************************************/ | |
226 | ||
227 | /* This function is called when we have to recalculate the size of a maildir by | |
228 | scanning all the files and directories therein. There are rules and conventions | |
229 | about which files or directories are included. We support this by the use of a | |
230 | regex to match directories that are to be included. | |
231 | ||
232 | Maildirs can only be one level deep. However, this function recurses, so it | |
233 | might cope with deeper nestings. We use the existing check_dir_size() function | |
234 | to add up the sizes of the files in a directory that contains messages. | |
235 | ||
236 | The function returns the most recent timestamp encountered. It can also be run | |
237 | in a dummy mode in which it does not scan for sizes, but just returns the | |
238 | timestamp. | |
239 | ||
240 | Arguments: | |
241 | path the path to the maildir | |
242 | filecount where to store the count of messages | |
243 | latest where to store the latest timestamp encountered | |
244 | regex a regex for getting files sizes from file names | |
245 | dir_regex a regex for matching directories to be included | |
246 | timestamp_only don't actually compute any sizes | |
247 | ||
248 | Returns: the sum of the sizes of the messages | |
249 | */ | |
250 | ||
0d7eb84a | 251 | off_t |
0756eb3c PH |
252 | maildir_compute_size(uschar *path, int *filecount, time_t *latest, |
253 | const pcre *regex, const pcre *dir_regex, BOOL timestamp_only) | |
254 | { | |
255 | DIR *dir; | |
0d7eb84a | 256 | off_t sum = 0; |
0756eb3c | 257 | |
54a2a2a9 | 258 | if (!(dir = exim_opendir(path))) |
f3ebb786 | 259 | return 0; |
0756eb3c | 260 | |
54a2a2a9 | 261 | for (struct dirent *ent; ent = readdir(dir); ) |
0756eb3c | 262 | { |
f3ebb786 | 263 | uschar * s, * name = US ent->d_name; |
54a2a2a9 | 264 | struct stat statbuf; |
0756eb3c PH |
265 | |
266 | if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue; | |
267 | ||
268 | /* We are normally supplied with a regex for choosing which directories to | |
269 | scan. We do the regex match first, because that avoids a stat() for names | |
270 | we aren't interested in. */ | |
271 | ||
272 | if (dir_regex != NULL && | |
273 | pcre_exec(dir_regex, NULL, CS name, Ustrlen(name), 0, 0, NULL, 0) < 0) | |
274 | { | |
275 | DEBUG(D_transport) | |
276 | debug_printf("skipping %s/%s: dir_regex does not match\n", path, name); | |
277 | continue; | |
278 | } | |
279 | ||
280 | /* The name is OK; stat it. */ | |
281 | ||
f3ebb786 JH |
282 | s = string_sprintf("%s/%s", path, name); |
283 | if (Ustat(s, &statbuf) < 0) | |
0756eb3c PH |
284 | { |
285 | DEBUG(D_transport) | |
286 | debug_printf("maildir_compute_size: stat error %d for %s: %s\n", errno, | |
f3ebb786 | 287 | s, strerror(errno)); |
0756eb3c PH |
288 | continue; |
289 | } | |
290 | ||
291 | if ((statbuf.st_mode & S_IFMT) != S_IFDIR) | |
292 | { | |
293 | DEBUG(D_transport) | |
f3ebb786 | 294 | debug_printf("skipping %s/%s: not a directory\n", s, name); |
0756eb3c PH |
295 | continue; |
296 | } | |
297 | ||
298 | /* Keep the latest timestamp encountered */ | |
299 | ||
300 | if (statbuf.st_mtime > *latest) *latest = statbuf.st_mtime; | |
301 | ||
302 | /* If this is a maildir folder, call this function recursively. */ | |
303 | ||
304 | if (name[0] == '.') | |
f3ebb786 | 305 | sum += maildir_compute_size(s, filecount, latest, regex, dir_regex, |
0756eb3c | 306 | timestamp_only); |
0756eb3c PH |
307 | |
308 | /* Otherwise it must be a folder that contains messages (e.g. new or cur), so | |
309 | we need to get its size, unless all we are interested in is the timestamp. */ | |
310 | ||
311 | else if (!timestamp_only) | |
f3ebb786 | 312 | sum += check_dir_size(s, filecount, regex); |
0756eb3c PH |
313 | } |
314 | ||
315 | closedir(dir); | |
316 | DEBUG(D_transport) | |
317 | { | |
318 | if (timestamp_only) | |
319 | debug_printf("maildir_compute_size (timestamp_only): %ld\n", | |
320 | (long int) *latest); | |
321 | else | |
b1c749bb PH |
322 | debug_printf("maildir_compute_size: path=%s\n sum=" OFF_T_FMT |
323 | " filecount=%d timestamp=%ld\n", | |
324 | path, sum, *filecount, (long int) *latest); | |
0756eb3c PH |
325 | } |
326 | return sum; | |
327 | } | |
328 | ||
329 | ||
330 | ||
331 | /************************************************* | |
332 | * Create or update maildirsizefile * | |
333 | *************************************************/ | |
334 | ||
335 | /* This function is called before a delivery if the option to use | |
336 | maildirsizefile is enabled. Its function is to create the file if it does not | |
337 | exist, or to update it if that is necessary. | |
338 | ||
339 | The logic in this function follows the rules that are described in | |
340 | ||
341 | http://www.inter7.com/courierimap/README.maildirquota.html | |
342 | ||
343 | Or, at least, it is supposed to! | |
344 | ||
345 | Arguments: | |
346 | path the path to the maildir directory; this is already backed-up | |
4c04137d | 347 | to the parent if the delivery directory is a maildirfolder |
0756eb3c PH |
348 | ob the appendfile options block |
349 | regex a compiled regex for getting a file's size from its name | |
350 | dir_regex a compiled regex for selecting maildir directories | |
351 | returned_size where to return the current size of the maildir, even if | |
352 | the maildirsizefile is removed because of a race | |
353 | ||
354 | Returns: >=0 a file descriptor for an open maildirsize file | |
355 | -1 there was an error opening or accessing the file | |
356 | -2 the file was removed because of a race | |
357 | */ | |
358 | ||
359 | int | |
360 | maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob, | |
0d7eb84a | 361 | const pcre *regex, const pcre *dir_regex, off_t *returned_size, |
0756eb3c PH |
362 | int *returned_filecount) |
363 | { | |
364 | int count, fd; | |
0d7eb84a | 365 | off_t cached_quota = 0; |
0756eb3c | 366 | int cached_quota_filecount = 0; |
0756eb3c PH |
367 | int filecount = 0; |
368 | int linecount = 0; | |
0d7eb84a | 369 | off_t size = 0; |
0756eb3c PH |
370 | uschar *filename; |
371 | uschar buffer[MAX_FILE_SIZE]; | |
372 | uschar *ptr = buffer; | |
373 | uschar *endptr; | |
374 | ||
375 | /* Try a few times to open or create the file, in case another process is doing | |
376 | the same thing. */ | |
377 | ||
378 | filename = string_sprintf("%s/maildirsize", path); | |
379 | ||
380 | DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path); | |
f3ebb786 | 381 | if ((fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600)) < 0) |
0756eb3c PH |
382 | { |
383 | if (errno != ENOENT) return -1; | |
384 | DEBUG(D_transport) | |
385 | debug_printf("%s does not exist: recalculating\n", filename); | |
386 | goto RECALCULATE; | |
387 | } | |
388 | ||
389 | /* The file has been successfully opened. Check that the cached quota value is | |
390 | still correct, and that the size of the file is still small enough. If so, | |
391 | compute the maildir size from the file. */ | |
392 | ||
f3ebb786 | 393 | if ((count = read(fd, buffer, sizeof(buffer))) >= sizeof(buffer)) |
0756eb3c PH |
394 | { |
395 | DEBUG(D_transport) | |
396 | debug_printf("maildirsize file too big (%d): recalculating\n", count); | |
397 | goto RECALCULATE; | |
398 | } | |
399 | buffer[count] = 0; /* Ensure string terminated */ | |
400 | ||
401 | /* Read the quota parameters from the first line of the data. */ | |
402 | ||
403 | DEBUG(D_transport) | |
404 | debug_printf("reading quota parameters from maildirsize data\n"); | |
405 | ||
406 | for (;;) | |
407 | { | |
0d7eb84a | 408 | off_t n = (off_t)Ustrtod(ptr, &endptr); |
0756eb3c PH |
409 | |
410 | /* Only two data items are currently defined; ignore any others that | |
411 | may be present. The spec is for a number followed by a letter. Anything | |
412 | else we reject and recalculate. */ | |
413 | ||
414 | if (*endptr == 'S') cached_quota = n; | |
0d7eb84a | 415 | else if (*endptr == 'C') cached_quota_filecount = (int)n; |
0756eb3c PH |
416 | if (!isalpha(*endptr++)) |
417 | { | |
418 | DEBUG(D_transport) | |
419 | debug_printf("quota parameter number not followed by letter in " | |
420 | "\"%.*s\": recalculating maildirsize\n", (int)(endptr - buffer), | |
421 | buffer); | |
422 | goto RECALCULATE; | |
423 | } | |
424 | if (*endptr == '\n' || *endptr == 0) break; | |
425 | if (*endptr++ != ',') | |
426 | { | |
427 | DEBUG(D_transport) | |
428 | debug_printf("quota parameter not followed by comma in " | |
429 | "\"%.*s\": recalculating maildirsize\n", (int)(endptr - buffer), | |
430 | buffer); | |
431 | goto RECALCULATE; | |
432 | } | |
433 | ptr = endptr; | |
434 | } | |
435 | ||
436 | /* Check the cached values against the current settings */ | |
437 | ||
438 | if (cached_quota != ob->quota_value || | |
439 | cached_quota_filecount != ob->quota_filecount_value) | |
440 | { | |
441 | DEBUG(D_transport) | |
442 | debug_printf("cached quota is out of date: recalculating\n" | |
b1c749bb PH |
443 | " quota=" OFF_T_FMT " cached_quota=" OFF_T_FMT " filecount_quota=%d " |
444 | "cached_quota_filecount=%d\n", ob->quota_value, | |
445 | cached_quota, ob->quota_filecount_value, cached_quota_filecount); | |
0756eb3c PH |
446 | goto RECALCULATE; |
447 | } | |
448 | ||
449 | /* Quota values agree; parse the rest of the data to get the sizes. At this | |
450 | stage, *endptr points either to 0 or to '\n'. */ | |
451 | ||
452 | DEBUG(D_transport) | |
453 | debug_printf("computing maildir size from maildirsize data\n"); | |
454 | ||
455 | while (*endptr++ == '\n') | |
456 | { | |
457 | if (*endptr == 0) break; | |
458 | linecount++; | |
459 | ptr = endptr; | |
0d7eb84a | 460 | size += (off_t)Ustrtod(ptr, &endptr); |
0756eb3c PH |
461 | if (*endptr != ' ') break; |
462 | ptr = endptr + 1; | |
463 | filecount += Ustrtol(ptr, &endptr, 10); | |
464 | } | |
465 | ||
466 | /* If *endptr is zero, we have successfully parsed the file, and we now have | |
467 | the size of the mailbox as cached in the file. The "rules" say that if this | |
468 | value indicates that the mailbox is over quota, we must recalculate if there is | |
62c0818f PH |
469 | more than one entry in the file, or if the file is older than 15 minutes. Also, |
470 | just in case there are weird values in the file, recalculate if either of the | |
471 | values is negative. */ | |
0756eb3c PH |
472 | |
473 | if (*endptr == 0) | |
474 | { | |
8e669ac1 | 475 | if (size < 0 || filecount < 0) |
62c0818f PH |
476 | { |
477 | DEBUG(D_transport) debug_printf("negative value in maildirsize " | |
b1c749bb | 478 | "(size=" OFF_T_FMT " count=%d): recalculating\n", size, filecount); |
8e669ac1 PH |
479 | goto RECALCULATE; |
480 | } | |
481 | ||
0756eb3c PH |
482 | if (ob->quota_value > 0 && |
483 | (size + (ob->quota_is_inclusive? message_size : 0) > ob->quota_value || | |
484 | (ob->quota_filecount_value > 0 && | |
485 | filecount + (ob->quota_is_inclusive ? 1:0) > | |
486 | ob->quota_filecount_value) | |
487 | )) | |
488 | { | |
489 | struct stat statbuf; | |
490 | if (linecount > 1) | |
491 | { | |
62c0818f | 492 | DEBUG(D_transport) debug_printf("over quota and maildirsize has " |
0756eb3c PH |
493 | "more than 1 entry: recalculating\n"); |
494 | goto RECALCULATE; | |
495 | } | |
496 | ||
497 | if (fstat(fd, &statbuf) < 0) goto RECALCULATE; /* Should never occur */ | |
498 | ||
499 | if (time(NULL) - statbuf.st_mtime > 15*60) | |
500 | { | |
501 | DEBUG(D_transport) debug_printf("over quota and maildirsize is older " | |
502 | "than 15 minutes: recalculating\n"); | |
503 | goto RECALCULATE; | |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | ||
509 | /* If *endptr is not zero, there was a syntax error in the file. */ | |
510 | ||
511 | else | |
512 | { | |
513 | int len; | |
514 | time_t old_latest, new_latest; | |
515 | uschar *tempname; | |
516 | struct timeval tv; | |
517 | ||
518 | DEBUG(D_transport) | |
519 | { | |
520 | uschar *p = endptr; | |
521 | while (p > buffer && p[-1] != '\n') p--; | |
522 | endptr[1] = 0; | |
523 | ||
524 | debug_printf("error in maildirsizefile: unexpected character %d in " | |
525 | "line %d (starting '%s'): recalculating\n", | |
526 | *endptr, linecount + 1, string_printing(p)); | |
527 | } | |
528 | ||
529 | /* Either there is no file, or the quota value has changed, or the file has | |
530 | got too big, or there was some format error in the file. Recalculate the size | |
531 | and write new contents to a temporary file; then rename it. After any | |
532 | error, just return -1 as the file descriptor. */ | |
533 | ||
534 | RECALCULATE: | |
535 | ||
f1e894f3 | 536 | if (fd >= 0) (void)close(fd); |
0756eb3c PH |
537 | old_latest = 0; |
538 | filecount = 0; | |
539 | size = maildir_compute_size(path, &filecount, &old_latest, regex, dir_regex, | |
540 | FALSE); | |
541 | ||
542 | (void)gettimeofday(&tv, NULL); | |
d0291a0a JH |
543 | tempname = string_sprintf("%s/tmp/" TIME_T_FMT ".H%luP%lu.%s", |
544 | path, tv.tv_sec, tv.tv_usec, (long unsigned) getpid(), primary_hostname); | |
0756eb3c | 545 | |
1da77999 | 546 | fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600); |
0756eb3c PH |
547 | if (fd >= 0) |
548 | { | |
b1c749bb PH |
549 | (void)sprintf(CS buffer, OFF_T_FMT "S,%dC\n" OFF_T_FMT " %d\n", |
550 | ob->quota_value, ob->quota_filecount_value, size, filecount); | |
0756eb3c PH |
551 | len = Ustrlen(buffer); |
552 | if (write(fd, buffer, len) != len || Urename(tempname, filename) < 0) | |
553 | { | |
f1e894f3 | 554 | (void)close(fd); |
0756eb3c PH |
555 | fd = -1; |
556 | } | |
557 | } | |
558 | ||
559 | /* If any of the directories have been modified since the last timestamp we | |
560 | saw, we have to junk this maildirsize file. */ | |
561 | ||
562 | DEBUG(D_transport) debug_printf("checking subdirectory timestamps\n"); | |
563 | new_latest = 0; | |
564 | (void)maildir_compute_size(path, NULL, &new_latest , NULL, dir_regex, TRUE); | |
565 | if (new_latest > old_latest) | |
566 | { | |
567 | DEBUG(D_transport) debug_printf("abandoning maildirsize because of " | |
568 | "a later subdirectory modification\n"); | |
569 | (void)Uunlink(filename); | |
f1e894f3 | 570 | (void)close(fd); |
660242ad | 571 | fd = -2; |
0756eb3c PH |
572 | } |
573 | } | |
574 | ||
575 | /* Return the sizes and the file descriptor, if any */ | |
576 | ||
b1c749bb PH |
577 | DEBUG(D_transport) debug_printf("returning maildir size=" OFF_T_FMT |
578 | " filecount=%d\n", size, filecount); | |
0756eb3c PH |
579 | *returned_size = size; |
580 | *returned_filecount = filecount; | |
581 | return fd; | |
582 | } | |
583 | ||
584 | /* End of tf_maildir.c */ |