X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/b1c749bb7f147e7f9215fe6067c848cf02938b92..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/transports/tf_maildir.c diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 5277ef092..925b8fac0 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -1,11 +1,11 @@ -/* $Cambridge: exim/src/src/transports/tf_maildir.c,v 1.6 2005/06/16 14:10:14 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions in support of the use of maildirsize files for handling quotas in maildir directories. Some of the rules are a bit baroque: @@ -29,46 +29,49 @@ calculations are not hard wired in, but are supplied as a regex. */ *************************************************/ /* This function is called at the start of a maildir delivery, to ensure that -all the relevant directories exist. +all the relevant directories exist. It also creates a maildirfolder file if the +base directory matches a given pattern. Argument: path the base directory name addr the address item (for setting an error message) create_directory true if we are allowed to create missing directories dirmode the mode for created directories + maildirfolder_create_regex + the pattern to match for maildirfolder creation Returns: TRUE on success; FALSE on failure */ BOOL maildir_ensure_directories(uschar *path, address_item *addr, - BOOL create_directory, int dirmode) + BOOL create_directory, int dirmode, uschar *maildirfolder_create_regex) { int i; struct stat statbuf; -char *subdirs[] = { "/tmp", "/new", "/cur" }; +const char *subdirs[] = { "/tmp", "/new", "/cur" }; DEBUG(D_transport) debug_printf("ensuring maildir directories exist in %s\n", path); /* First ensure that the path we have is a directory; if it does not exist, create it. Then make sure the tmp, new & cur subdirs of the maildir are -there. If not, fail which aborts the delivery (even though the cur subdir is +there. If not, fail. This aborts the delivery (even though the cur subdir is not actually needed for delivery). Handle all 4 directory tests/creates in a loop so that code can be shared. */ for (i = 0; i < 4; i++) { int j; - uschar *dir, *mdir; + const uschar *dir, *mdir; if (i == 0) { - mdir = US""; + mdir = CUS""; dir = path; } else { - mdir = US subdirs[i-1]; + mdir = CUS subdirs[i-1]; dir = mdir + 1; } @@ -135,7 +138,46 @@ for (i = 0; i < 4; i++) } } -return TRUE; /* All directories exist */ +/* If the basic path matches maildirfolder_create_regex, we are dealing with +a subfolder, and should ensure that a maildirfolder file exists. */ + +if (maildirfolder_create_regex) + { + const pcre2_code * re; + + DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n"); + + if (!(re = regex_compile(maildirfolder_create_regex, + MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx))) + return FALSE; + + if (regex_match(re, path, -1, NULL)) + { + uschar *fname = string_sprintf("%s/maildirfolder", path); + if (Ustat(fname, &statbuf) == 0) + { + DEBUG(D_transport) debug_printf("maildirfolder already exists\n"); + } + else + { + int fd = Uopen(fname, O_WRONLY|O_APPEND|O_CREAT, 0600); + if (fd < 0) + { + addr->message = string_sprintf("appendfile: failed to create " + "maildirfolder file in %s directory: %s", path, strerror(errno)); + return FALSE; + } + (void)close(fd); + DEBUG(D_transport) debug_printf("created maildirfolder file\n"); + } + } + else + { + DEBUG(D_transport) debug_printf("maildirfolder file not required\n"); + } + } + +return TRUE; /* Everything exists that should exist */ } @@ -162,10 +204,12 @@ int len; uschar buffer[256]; sprintf(CS buffer, "%d 1\n", size); len = Ustrlen(buffer); -(void)lseek(fd, 0, SEEK_END); -(void)write(fd, buffer, len); -DEBUG(D_transport) - debug_printf("added '%.*s' to maildirsize file\n", len-1, buffer); +if (lseek(fd, 0, SEEK_END) >= 0) + { + len = write(fd, buffer, len); + DEBUG(D_transport) + debug_printf("added '%.*s' to maildirsize file\n", len-1, buffer); + } } @@ -200,20 +244,18 @@ Returns: the sum of the sizes of the messages off_t maildir_compute_size(uschar *path, int *filecount, time_t *latest, - const pcre *regex, const pcre *dir_regex, BOOL timestamp_only) + const pcre2_code *regex, const pcre2_code *dir_regex, BOOL timestamp_only) { DIR *dir; off_t sum = 0; -struct dirent *ent; -struct stat statbuf; -dir = opendir(CS path); -if (dir == NULL) return 0; +if (!(dir = exim_opendir(path))) + return 0; -while ((ent = readdir(dir)) != NULL) +for (struct dirent *ent; ent = readdir(dir); ) { - uschar *name = US ent->d_name; - uschar buffer[1024]; + uschar * s, * name = US ent->d_name; + struct stat statbuf; if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue; @@ -221,8 +263,7 @@ while ((ent = readdir(dir)) != NULL) scan. We do the regex match first, because that avoids a stat() for names we aren't interested in. */ - if (dir_regex != NULL && - pcre_exec(dir_regex, NULL, CS name, Ustrlen(name), 0, 0, NULL, 0) < 0) + if (dir_regex && !regex_match(dir_regex, name, -1, NULL)) { DEBUG(D_transport) debug_printf("skipping %s/%s: dir_regex does not match\n", path, name); @@ -231,26 +272,19 @@ while ((ent = readdir(dir)) != NULL) /* The name is OK; stat it. */ - if (!string_format(buffer, sizeof(buffer), "%s/%s", path, name)) - { - DEBUG(D_transport) - debug_printf("maildir_compute_size: name too long: dir=%s name=%s\n", - path, name); - continue; - } - - if (Ustat(buffer, &statbuf) < 0) + s = string_sprintf("%s/%s", path, name); + if (Ustat(s, &statbuf) < 0) { DEBUG(D_transport) debug_printf("maildir_compute_size: stat error %d for %s: %s\n", errno, - buffer, strerror(errno)); + s, strerror(errno)); continue; } if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { DEBUG(D_transport) - debug_printf("skipping %s/%s: not a directory\n", path, name); + debug_printf("skipping %s/%s: not a directory\n", s, name); continue; } @@ -261,18 +295,14 @@ while ((ent = readdir(dir)) != NULL) /* If this is a maildir folder, call this function recursively. */ if (name[0] == '.') - { - sum += maildir_compute_size(buffer, filecount, latest, regex, dir_regex, + sum += maildir_compute_size(s, filecount, latest, regex, dir_regex, timestamp_only); - } /* Otherwise it must be a folder that contains messages (e.g. new or cur), so we need to get its size, unless all we are interested in is the timestamp. */ else if (!timestamp_only) - { - sum += check_dir_size(buffer, filecount, regex); - } + sum += check_dir_size(s, filecount, regex); } closedir(dir); @@ -307,7 +337,7 @@ Or, at least, it is supposed to! Arguments: path the path to the maildir directory; this is already backed-up - to the parent if the delivery diretory is a maildirfolder + to the parent if the delivery directory is a maildirfolder ob the appendfile options block regex a compiled regex for getting a file's size from its name dir_regex a compiled regex for selecting maildir directories @@ -321,7 +351,7 @@ Returns: >=0 a file descriptor for an open maildirsize file int maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob, - const pcre *regex, const pcre *dir_regex, off_t *returned_size, + const pcre2_code *regex, const pcre2_code *dir_regex, off_t *returned_size, int *returned_filecount) { int count, fd; @@ -341,8 +371,7 @@ the same thing. */ filename = string_sprintf("%s/maildirsize", path); DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path); -fd = Uopen(filename, O_RDWR|O_APPEND, 0); -if (fd < 0) +if ((fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600)) < 0) { if (errno != ENOENT) return -1; DEBUG(D_transport) @@ -354,8 +383,7 @@ if (fd < 0) still correct, and that the size of the file is still small enough. If so, compute the maildir size from the file. */ -count = read(fd, buffer, sizeof(buffer)); -if (count >= sizeof(buffer)) +if ((count = read(fd, buffer, sizeof(buffer))) >= sizeof(buffer)) { DEBUG(D_transport) debug_printf("maildirsize file too big (%d): recalculating\n", count); @@ -498,17 +526,17 @@ else RECALCULATE: - if (fd >= 0) close(fd); + if (fd >= 0) (void)close(fd); old_latest = 0; filecount = 0; size = maildir_compute_size(path, &filecount, &old_latest, regex, dir_regex, FALSE); (void)gettimeofday(&tv, NULL); - tempname = string_sprintf("%s/tmp/%lu.H%luP%lu.%s", path, tv.tv_sec, - tv.tv_usec, getpid(), primary_hostname); + tempname = string_sprintf("%s/tmp/" TIME_T_FMT ".H%luP%lu.%s", + path, tv.tv_sec, tv.tv_usec, (long unsigned) getpid(), primary_hostname); - fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, 0600); + fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600); if (fd >= 0) { (void)sprintf(CS buffer, OFF_T_FMT "S,%dC\n" OFF_T_FMT " %d\n", @@ -516,7 +544,7 @@ else len = Ustrlen(buffer); if (write(fd, buffer, len) != len || Urename(tempname, filename) < 0) { - close(fd); + (void)close(fd); fd = -1; } } @@ -532,8 +560,8 @@ else DEBUG(D_transport) debug_printf("abandoning maildirsize because of " "a later subdirectory modification\n"); (void)Uunlink(filename); - close(fd); - fd = -1; + (void)close(fd); + fd = -2; } }