static uint32_t mailbox_uidvalidity_next_rescan(struct mailbox_list *list, const char *path) { DIR *d; struct dirent *dp; const char *fname, *dir, *prefix, *tmp; unsigned int i, prefix_len; uint32_t cur_value, min_value, max_value; mode_t old_mask; int fd; fname = strrchr(path, '/'); if (fname == NULL) { dir = "."; fname = path; } else { dir = t_strdup_until(path, fname); fname++; } d = opendir(dir); if (d == NULL && errno == ENOENT) { /* FIXME: the PATH_TYPE_CONTROL should come as a parameter, but that's an API change, do it in v2.3. it's not really a problem though, since currently all backends use control dirs for the uidvalidity file. */ (void)mailbox_list_mkdir_root(list, dir, MAILBOX_LIST_PATH_TYPE_CONTROL); d = opendir(dir); } if (d == NULL) { i_error("opendir(%s) failed: %m", dir); return mailbox_uidvalidity_next_fallback(); } prefix = t_strconcat(fname, ".", NULL); prefix_len = strlen(prefix); /* just in case there happens to be multiple matching uidvalidity files, track the min/max values. use the max value and delete the min value file. */ max_value = 0; min_value = (uint32_t)-1; while ((dp = readdir(d)) != NULL) { if (strncmp(dp->d_name, prefix, prefix_len) == 0) { if (str_to_uint32_hex(dp->d_name + prefix_len, &cur_value) >= 0) { if (min_value > cur_value) min_value = cur_value; if (max_value < cur_value) max_value = cur_value; } } } if (closedir(d) < 0) i_error("closedir(%s) failed: %m", dir); if (max_value == 0) { /* no uidvalidity files. create one. */ for (i = 0; i < RETRY_COUNT; i++) { cur_value = mailbox_uidvalidity_next_fallback(); tmp = t_strdup_printf("%s.%08x", path, cur_value); /* the file is empty, don't bother with permissions */ old_mask = umask(0); fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0444); umask(old_mask); if (fd != -1 || errno != EEXIST) break; /* already exists. although it's quite unlikely we'll hit this race condition. more likely we'll create a duplicate file.. */ } if (fd == -1) { i_error("creat(%s) failed: %m", tmp); return cur_value; } i_close_fd(&fd); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; } if (min_value != max_value) { /* duplicate uidvalidity files, delete the oldest */ tmp = t_strdup_printf("%s.%08x", path, min_value); i_unlink_if_exists(tmp); } cur_value = max_value; if (mailbox_uidvalidity_rename(path, &cur_value, TRUE) < 0) return mailbox_uidvalidity_next_fallback(); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; }
static uint32_t mailbox_uidvalidity_next_rescan(struct mailbox_list *list, const char *path) { DIR *d; struct dirent *dp; const char *fname, *dir, *prefix, *tmp; char *endp; unsigned int i, prefix_len; uint32_t cur_value, min_value, max_value; mode_t old_mask; int fd; fname = strrchr(path, '/'); if (fname == NULL) { dir = "."; fname = path; } else { dir = t_strdup_until(path, fname); fname++; } d = opendir(dir); if (d == NULL) { i_error("opendir(%s) failed: %m", dir); return mailbox_uidvalidity_next_fallback(); } prefix = t_strconcat(fname, ".", NULL); prefix_len = strlen(prefix); /* just in case there happens to be multiple matching uidvalidity files, track the min/max values. use the max value and delete the min value file. */ max_value = 0; min_value = (uint32_t)-1; while ((dp = readdir(d)) != NULL) { if (strncmp(dp->d_name, prefix, prefix_len) == 0) { cur_value = strtoul(dp->d_name + prefix_len, &endp, 16); if (*endp == '\0') { if (min_value > cur_value) min_value = cur_value; if (max_value < cur_value) max_value = cur_value; } } } if (closedir(d) < 0) i_error("closedir(%s) failed: %m", dir); if (max_value == 0) { /* no uidvalidity files. create one. */ for (i = 0; i < RETRY_COUNT; i++) { cur_value = mailbox_uidvalidity_next_fallback(); tmp = t_strdup_printf("%s.%08x", path, cur_value); /* the file is empty, don't bother with permissions */ old_mask = umask(0); fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0444); umask(old_mask); if (fd != -1 || errno != EEXIST) break; /* already exists. although it's quite unlikely we'll hit this race condition. more likely we'll create a duplicate file.. */ } if (fd == -1) { i_error("creat(%s) failed: %m", tmp); return cur_value; } i_close_fd(&fd); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; } if (min_value != max_value) { /* duplicate uidvalidity files, delete the oldest */ tmp = t_strdup_printf("%s.%08x", path, min_value); if (unlink(tmp) < 0 && errno != ENOENT) i_error("unlink(%s) failed: %m", tmp); } cur_value = max_value; if (mailbox_uidvalidity_rename(path, &cur_value, TRUE) < 0) return mailbox_uidvalidity_next_fallback(); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; }