static int settings_add_include(const char *path, struct input_stack **inputp, bool ignore_errors, const char **error_r) { struct input_stack *tmp, *new_input; int fd; for (tmp = *inputp; tmp != NULL; tmp = tmp->prev) { if (strcmp(tmp->path, path) == 0) break; } if (tmp != NULL) { *error_r = t_strdup_printf("Recursive include file: %s", path); return -1; } if ((fd = open(path, O_RDONLY)) == -1) { if (ignore_errors) return 0; *error_r = t_strdup_printf("Couldn't open include file %s: %m", path); return -1; } new_input = t_new(struct input_stack, 1); new_input->prev = *inputp; new_input->path = t_strdup(path); new_input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(new_input->input, TRUE); *inputp = new_input; return 0; }
struct subsfile_list_context * subsfile_list_init(struct mailbox_list *list, const char *path) { struct subsfile_list_context *ctx; int fd; ctx = i_new(struct subsfile_list_context, 1); ctx->list = list; fd = nfs_safe_open(path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) { subsread_set_syscall_error(list, "open()", path); ctx->failed = TRUE; } } else { ctx->input = i_stream_create_fd_autoclose(&fd, list->mailbox_name_max_length+1); i_stream_set_return_partial_line(ctx->input, TRUE); subsfile_list_read_header(ctx->list, ctx->input, &ctx->version); } ctx->path = i_strdup(path); ctx->name = str_new(default_pool, 128); return ctx; }
static void run_cmd(struct dsync_cmd_context *ctx, const char *const *args) { int fd_in[2], fd_out[2], fd_err[2]; if (pipe(fd_in) < 0 || pipe(fd_out) < 0 || pipe(fd_err) < 0) i_fatal("pipe() failed: %m"); ctx->remote_pid = fork(); switch (ctx->remote_pid) { case -1: i_fatal("fork() failed: %m"); case 0: /* child, which will execute the proxy server. stdin/stdout goes to pipes which we'll pass to proxy client. */ if (dup2(fd_in[0], STDIN_FILENO) < 0 || dup2(fd_out[1], STDOUT_FILENO) < 0 || dup2(fd_err[1], STDERR_FILENO) < 0) i_fatal("dup2() failed: %m"); i_close_fd(&fd_in[0]); i_close_fd(&fd_in[1]); i_close_fd(&fd_out[0]); i_close_fd(&fd_out[1]); i_close_fd(&fd_err[0]); i_close_fd(&fd_err[1]); execvp_const(args[0], args); default: /* parent */ break; } i_close_fd(&fd_in[0]); i_close_fd(&fd_out[1]); i_close_fd(&fd_err[1]); ctx->fd_in = fd_out[0]; ctx->fd_out = fd_in[1]; ctx->fd_err = fd_err[0]; if (ctx->remote_user_prefix) { const char *prefix = t_strdup_printf("%s\n", ctx->ctx.cur_username); if (write_full(ctx->fd_out, prefix, strlen(prefix)) < 0) i_fatal("write(remote out) failed: %m"); } fd_set_nonblock(ctx->fd_err, TRUE); ctx->err_stream = i_stream_create_fd(ctx->fd_err, IO_BLOCK_SIZE, FALSE); i_stream_set_return_partial_line(ctx->err_stream, TRUE); }
const char *subsfile_list_next(struct subsfile_list_context *ctx) { const char *line; unsigned int i; int fd; if (ctx->failed || ctx->input == NULL) return NULL; for (i = 0;; i++) { line = next_line(ctx->list, ctx->path, ctx->input, &ctx->failed, i < SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT); if (ctx->input->stream_errno != ESTALE || i == SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT) break; /* Reopen the subscription file and re-send everything. this isn't the optimal behavior, but it's allowed by IMAP and this way we don't have to read everything into memory or try to play any guessing games. */ i_stream_destroy(&ctx->input); fd = nfs_safe_open(ctx->path, O_RDONLY); if (fd == -1) { /* In case of ENOENT all the subscriptions got lost. Just return end of subscriptions list in that case. */ if (errno != ENOENT) { subsread_set_syscall_error(ctx->list, "open()", ctx->path); ctx->failed = TRUE; } return NULL; } ctx->input = i_stream_create_fd_autoclose(&fd, ctx->list->mailbox_name_max_length+1); i_stream_set_return_partial_line(ctx->input, TRUE); } if (ctx->version > 1 && line != NULL) line = subsfile_list_unescaped(ctx, line); return line; }
int subsfile_set_subscribed(struct mailbox_list *list, const char *path, const char *temp_prefix, const char *name, bool set) { const struct mail_storage_settings *mail_set = list->mail_set; struct dotlock_settings dotlock_set; struct dotlock *dotlock; struct mailbox_permissions perm; const char *line, *dir, *fname, *escaped_name; struct istream *input = NULL; struct ostream *output; int fd_in, fd_out; enum mailbox_list_path_type type; bool found, changed = FALSE, failed = FALSE; unsigned int version = 2; if (strcasecmp(name, "INBOX") == 0) name = "INBOX"; memset(&dotlock_set, 0, sizeof(dotlock_set)); dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; dotlock_set.nfs_flush = mail_set->mail_nfs_storage; dotlock_set.temp_prefix = temp_prefix; dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT; dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT; mailbox_list_get_root_permissions(list, &perm); fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); if (fd_out == -1 && errno == ENOENT) { /* directory hasn't been created yet. */ type = list->set.control_dir != NULL ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; fname = strrchr(path, '/'); if (fname != NULL) { dir = t_strdup_until(path, fname); if (mailbox_list_mkdir_root(list, dir, type) < 0) return -1; } fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); } if (fd_out == -1) { if (errno == EAGAIN) { mailbox_list_set_error(list, MAIL_ERROR_TEMP, "Timeout waiting for subscription file lock"); } else { subswrite_set_syscall_error(list, "file_dotlock_open()", path); } return -1; } fd_in = nfs_safe_open(path, O_RDONLY); if (fd_in == -1 && errno != ENOENT) { subswrite_set_syscall_error(list, "open()", path); file_dotlock_delete(&dotlock); return -1; } if (fd_in != -1) { input = i_stream_create_fd_autoclose(&fd_in, list->mailbox_name_max_length+1); i_stream_set_return_partial_line(input, TRUE); subsfile_list_read_header(list, input, &version); } found = FALSE; output = o_stream_create_fd_file(fd_out, 0, FALSE); o_stream_cork(output); if (version >= 2) o_stream_send_str(output, version2_header); if (version < 2 || name[0] == '\0') escaped_name = name; else { const char *const *tmp; char separators[2]; string_t *str = t_str_new(64); separators[0] = mailbox_list_get_hierarchy_sep(list); separators[1] = '\0'; tmp = t_strsplit(name, separators); str_append_tabescaped(str, *tmp); for (tmp++; *tmp != NULL; tmp++) { str_append_c(str, '\t'); str_append_tabescaped(str, *tmp); } escaped_name = str_c(str); } if (input != NULL) { while ((line = next_line(list, path, input, &failed, FALSE)) != NULL) { if (strcmp(line, escaped_name) == 0) { found = TRUE; if (!set) { changed = TRUE; continue; } } o_stream_nsend_str(output, line); o_stream_nsend(output, "\n", 1); } i_stream_destroy(&input); } if (!failed && set && !found) { /* append subscription */ line = t_strconcat(escaped_name, "\n", NULL); o_stream_nsend_str(output, line); changed = TRUE; } if (changed && !failed) { if (o_stream_nfinish(output) < 0) { subswrite_set_syscall_error(list, "write()", path); failed = TRUE; } else if (mail_set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fsync(fd_out) < 0) { subswrite_set_syscall_error(list, "fsync()", path); failed = TRUE; } } } else { o_stream_ignore_last_errors(output); } o_stream_destroy(&output); if (failed || !changed) { if (file_dotlock_delete(&dotlock) < 0) { subswrite_set_syscall_error(list, "file_dotlock_delete()", path); failed = TRUE; } } else { enum dotlock_replace_flags flags = DOTLOCK_REPLACE_FLAG_VERIFY_OWNER; if (file_dotlock_replace(&dotlock, flags) < 0) { subswrite_set_syscall_error(list, "file_dotlock_replace()", path); failed = TRUE; } } return failed ? -1 : (changed ? 1 : 0); }
bool settings_read_i(const char *path, const char *section, settings_callback_t *callback, settings_section_callback_t *sect_callback, void *context, const char **error_r) { /* pretty horrible code, but v2.0 will have this rewritten anyway.. */ struct input_stack root, *input; const char *errormsg, *next_section, *name, *last_section_path = NULL; char *line, *key, *p, quote; string_t *full_line; size_t len; int fd, last_section_line = 0, skip, sections, root_section; fd = open(path, O_RDONLY); if (fd < 0) { *error_r = t_strdup_printf( "Can't open configuration file %s: %m", path); return FALSE; } if (section == NULL) { skip = 0; next_section = NULL; } else { skip = 1; next_section = t_strcut(section, '/'); } memset(&root, 0, sizeof(root)); root.path = path; input = &root; full_line = t_str_new(512); sections = 0; root_section = 0; errormsg = NULL; input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(input->input, TRUE); prevfile: while ((line = i_stream_read_next_line(input->input)) != NULL) { input->linenum++; /* @UNSAFE: line is modified */ /* skip whitespace */ while (IS_WHITE(*line)) line++; /* ignore comments or empty lines */ if (*line == '#' || *line == '\0') continue; /* strip away comments. pretty kludgy way really.. */ for (p = line; *p != '\0'; p++) { if (*p == '\'' || *p == '"') { quote = *p; for (p++; *p != quote && *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') p++; } if (*p == '\0') break; } else if (*p == '#') { if (!IS_WHITE(p[-1])) { i_warning("Configuration file %s line %u: " "Ambiguous '#' character in line, treating it as comment. " "Add a space before it to remove this warning.", input->path, input->linenum); } *p = '\0'; break; } } /* remove whitespace from end of line */ len = strlen(line); while (IS_WHITE(line[len-1])) len--; line[len] = '\0'; if (len > 0 && line[len-1] == '\\') { /* continues in next line */ len--; while (IS_WHITE(line[len-1])) len--; str_append_n(full_line, line, len); str_append_c(full_line, ' '); continue; } if (str_len(full_line) > 0) { str_append(full_line, line); line = str_c_modifiable(full_line); } /* a) key = value b) section_type [section_name] { c) } */ key = line; while (!IS_WHITE(*line) && *line != '\0' && *line != '=') line++; if (IS_WHITE(*line)) { *line++ = '\0'; while (IS_WHITE(*line)) line++; } if (strcmp(key, "!include_try") == 0 || strcmp(key, "!include") == 0) { if (settings_include(fix_relative_path(line, input), &input, strcmp(key, "!include_try") == 0, &errormsg) == 0) goto prevfile; } else if (*line == '=') { /* a) */ *line++ = '\0'; while (IS_WHITE(*line)) line++; len = strlen(line); if (len > 0 && ((*line == '"' && line[len-1] == '"') || (*line == '\'' && line[len-1] == '\''))) { line[len-1] = '\0'; line = str_unescape(line+1); } errormsg = skip ? NULL : callback(key, line, context); } else if (strcmp(key, "}") != 0 || *line != '\0') { /* b) + errors */ line[-1] = '\0'; if (*line == '{') name = ""; else { name = line; while (!IS_WHITE(*line) && *line != '\0') line++; if (*line != '\0') { *line++ = '\0'; while (IS_WHITE(*line)) line++; } } if (*line != '{') errormsg = "Expecting '='"; else { sections++; if (next_section != NULL && strcmp(next_section, name) == 0) { section += strlen(next_section); if (*section == '\0') { skip = 0; next_section = NULL; root_section = sections; } else { i_assert(*section == '/'); section++; next_section = t_strcut(section, '/'); } } if (skip > 0) skip++; else { skip = sect_callback == NULL ? 1 : !sect_callback(key, name, context, &errormsg); if (errormsg != NULL && last_section_line != 0) { errormsg = t_strdup_printf( SECTION_ERRORMSG, errormsg, last_section_path, last_section_line); } } last_section_path = input->path; last_section_line = input->linenum; } } else { /* c) */ if (sections == 0) errormsg = "Unexpected '}'"; else { if (skip > 0) skip--; else { i_assert(sect_callback != NULL); sect_callback(NULL, NULL, context, &errormsg); if (root_section == sections && errormsg == NULL) { /* we found the section, now quit */ break; } } last_section_path = input->path; last_section_line = input->linenum; sections--; } } if (errormsg != NULL) { *error_r = t_strdup_printf( "Error in configuration file %s line %d: %s", input->path, input->linenum, errormsg); break; } str_truncate(full_line, 0); } i_stream_destroy(&input->input); input = input->prev; if (line == NULL && input != NULL) goto prevfile; return errormsg == NULL; }
static int passwd_file_open(struct passwd_file *pw, bool startup, const char **error_r) { const char *no_args = NULL; struct istream *input; const char *line; struct stat st; time_t start_time, end_time; unsigned int time_secs; int fd; fd = open(pw->path, O_RDONLY); if (fd == -1) { if (errno == EACCES) *error_r = eacces_error_get("open", pw->path); else { *error_r = t_strdup_printf("open(%s) failed: %m", pw->path); } return -1; } if (fstat(fd, &st) != 0) { *error_r = t_strdup_printf("fstat(%s) failed: %m", pw->path); i_close_fd(&fd); return -1; } pw->fd = fd; pw->stamp = st.st_mtime; pw->size = st.st_size; pw->pool = pool_alloconly_create(MEMPOOL_GROWING"passwd_file", 10240); hash_table_create(&pw->users, pw->pool, 0, str_hash, strcmp); start_time = time(NULL); input = i_stream_create_fd(pw->fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(input, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { if (*line == '\0' || *line == ':' || *line == '#') continue; /* no username or comment */ T_BEGIN { const char *const *args = t_strsplit(line, ":"); if (args[1] != NULL) { /* at least username+password */ passwd_file_add(pw, args[0], args[1], args+2); } else { /* only username */ passwd_file_add(pw, args[0], NULL, &no_args); } } T_END; } i_stream_destroy(&input); end_time = time(NULL); time_secs = end_time - start_time; if ((time_secs > PARSE_TIME_STARTUP_WARN_SECS && startup) || (time_secs > PARSE_TIME_RELOAD_WARN_SECS && !startup)) { i_warning("passwd-file %s: Reading %u users took %u secs", pw->path, hash_table_count(pw->users), time_secs); } else if (pw->db->debug) { i_debug("passwd-file %s: Read %u users in %u secs", pw->path, hash_table_count(pw->users), time_secs); } return 0; }
static int acl_backend_vfile_read(struct acl_object *aclobj, bool global, const char *path, struct acl_vfile_validity *validity, bool try_retry, bool *is_dir_r) { struct istream *input; struct stat st; struct acl_rights rights; const char *line, *error; unsigned int linenum; int fd, ret = 0; *is_dir_r = FALSE; fd = nfs_safe_open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT || errno == ENOTDIR) { if (aclobj->backend->debug) i_debug("acl vfile: file %s not found", path); validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND; } else if (errno == EACCES) { if (aclobj->backend->debug) i_debug("acl vfile: no access to file %s", path); acl_object_remove_all_access(aclobj); validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS; } else { i_error("open(%s) failed: %m", path); return -1; } validity->last_size = 0; validity->last_read_time = ioloop_time; return 1; } if (fstat(fd, &st) < 0) { if (errno == ESTALE && try_retry) { i_close_fd(&fd); return 0; } i_error("fstat(%s) failed: %m", path); i_close_fd(&fd); return -1; } if (S_ISDIR(st.st_mode)) { /* we opened a directory. */ *is_dir_r = TRUE; i_close_fd(&fd); return 0; } if (aclobj->backend->debug) i_debug("acl vfile: reading file %s", path); input = i_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(input, TRUE); linenum = 1; while ((line = i_stream_read_next_line(input)) != NULL) { T_BEGIN { ret = acl_rights_parse_line(line, aclobj->rights_pool, &rights, &error); rights.global = global; if (ret < 0) { i_error("ACL file %s line %u: %s", path, linenum, error); } else { array_append(&aclobj->rights, &rights, 1); } } T_END; if (ret < 0) break; linenum++; } if (ret < 0) { /* parsing failure */ } else if (input->stream_errno != 0) { if (input->stream_errno == ESTALE && try_retry) ret = 0; else { ret = -1; i_error("read(%s) failed: %m", path); } } else { if (fstat(fd, &st) < 0) { if (errno == ESTALE && try_retry) ret = 0; else { ret = -1; i_error("fstat(%s) failed: %m", path); } } else { ret = 1; validity->last_read_time = ioloop_time; validity->last_mtime = st.st_mtime; validity->last_size = st.st_size; } } i_stream_unref(&input); if (close(fd) < 0) { if (errno == ESTALE && try_retry) return 0; i_error("close(%s) failed: %m", path); return -1; } return ret; }