struct istream *iostream_temp_finish(struct ostream **output, size_t max_buffer_size) { struct temp_ostream *tstream = (struct temp_ostream *)(*output)->real_stream; struct istream *input, *input2; uoff_t abs_offset, size; const char *for_path; int fd; if (tstream->name[0] == '\0') for_path = ""; else for_path = t_strdup_printf(" for %s", tstream->name); if (tstream->dupstream != NULL && !tstream->dupstream->closed) { abs_offset = i_stream_get_absolute_offset(tstream->dupstream) - tstream->dupstream->v_offset + tstream->dupstream_start_offset; size = tstream->dupstream_offset - tstream->dupstream_start_offset; fd = dup(i_stream_get_fd(tstream->dupstream)); if (fd == -1) input = i_stream_create_error_str(errno, "dup() failed: %m"); else { input2 = i_stream_create_fd_autoclose(&fd, max_buffer_size); i_stream_seek(input2, abs_offset); input = i_stream_create_limit(input2, size); i_stream_unref(&input2); } i_stream_set_name(input, t_strdup_printf( "(Temp file in %s%s, from %s)", tstream->temp_path_prefix, for_path, i_stream_get_name(tstream->dupstream))); i_stream_unref(&tstream->dupstream); } else if (tstream->dupstream != NULL) { /* return the original failed stream. */ input = tstream->dupstream; } else if (tstream->fd != -1) { int fd = tstream->fd; input = i_stream_create_fd_autoclose(&tstream->fd, max_buffer_size); i_stream_set_name(input, t_strdup_printf( "(Temp file fd %d in %s%s, %"PRIuUOFF_T" bytes)", fd, tstream->temp_path_prefix, for_path, tstream->fd_size)); } else { input = i_stream_create_from_data(tstream->buf->data, tstream->buf->used); i_stream_set_name(input, t_strdup_printf( "(Temp buffer in %s%s, %"PRIuSIZE_T" bytes)", tstream->temp_path_prefix, for_path, tstream->buf->used)); i_stream_add_destroy_callback(input, iostream_temp_buf_destroyed, tstream->buf); tstream->buf = NULL; } o_stream_destroy(output); return input; }
static void cmd_dump_imapzlib(int argc ATTR_UNUSED, char *argv[]) { struct istream *input, *input2; const unsigned char *data; size_t size; const char *line; int fd; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); input = i_stream_create_fd_autoclose(&fd, 1024*32); while ((line = i_stream_read_next_line(input)) != NULL) { /* skip tag */ printf("%s\r\n", line); while (*line != ' ' && *line != '\0') line++; if (*line == '\0') continue; line++; if (strcmp(line, "OK Begin compression.") == 0 || strcasecmp(line, "COMPRESS DEFLATE") == 0) break; } input2 = i_stream_create_deflate(input, TRUE); i_stream_unref(&input); while (i_stream_read_more(input2, &data, &size) != -1) { fwrite(data, 1, size, stdout); i_stream_skip(input2, size); } i_stream_unref(&input2); fflush(stdout); }
static struct userdb_iterate_context * passwd_file_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct userdb_module *_module = auth_request->userdb->userdb; struct passwd_file_userdb_module *module = (struct passwd_file_userdb_module *)_module; struct passwd_file_userdb_iterate_context *ctx; int fd; ctx = i_new(struct passwd_file_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; ctx->skip_passdb_entries = !module->pwf->userdb_warn_missing; if (module->pwf->default_file == NULL) { i_error("passwd-file: User iteration isn't currently supported " "with %%variable paths"); ctx->ctx.failed = TRUE; return &ctx->ctx; } ctx->path = i_strdup(module->pwf->default_file->path); /* for now we support only a single passwd-file */ fd = open(ctx->path, O_RDONLY); if (fd == -1) { i_error("open(%s) failed: %m", ctx->path); ctx->ctx.failed = TRUE; } else { ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); } return &ctx->ctx; }
static void director_connect(struct director_context *ctx) { #define DIRECTOR_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" const char *line; int fd; fd = doveadm_connect(ctx->socket_path); net_set_nonblock(fd, FALSE); ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); director_send(ctx, DIRECTOR_HANDSHAKE); alarm(5); line = i_stream_read_next_line(ctx->input); alarm(0); if (line == NULL) { if (ctx->input->stream_errno != 0) i_fatal("read(%s) failed: %m", ctx->socket_path); else if (ctx->input->eof) i_fatal("%s disconnected", ctx->socket_path); else { i_fatal("read(%s) timed out (is director configured?)", ctx->socket_path); } } if (!version_string_verify(line, "director-doveadm", 1)) { i_fatal_status(EX_PROTOCOL, "%s not a compatible director-doveadm socket", ctx->socket_path); } }
static void penalty_lookup(struct penalty_context *ctx) { #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" #define ANVIL_CMD ANVIL_HANDSHAKE"PENALTY-DUMP\n" struct istream *input; const char *line; int fd; fd = doveadm_connect(ctx->anvil_path); net_set_nonblock(fd, FALSE); if (write(fd, ANVIL_CMD, strlen(ANVIL_CMD)) < 0) i_fatal("write(%s) failed: %m", ctx->anvil_path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { if (*line == '\0') break; T_BEGIN { struct penalty_line penalty_line; penalty_parse_line(line, &penalty_line); penalty_print_line(ctx, &penalty_line); } T_END; } if (input->stream_errno != 0) i_fatal("read(%s) failed: %m", ctx->anvil_path); i_stream_destroy(&input); }
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 test_compress_file(const char *in_path, const char *out_path) { const struct compression_handler *handler; struct istream *input, *file_input; struct ostream *output, *file_output; int fd_in, fd_out; struct sha1_ctxt sha1; unsigned char output_sha1[SHA1_RESULTLEN], input_sha1[SHA1_RESULTLEN]; const unsigned char *data; size_t size; ssize_t ret; handler = compression_lookup_handler_from_ext(out_path); if (handler == NULL) i_fatal("Can't detect compression algorithm from path %s", out_path); if (handler->create_ostream == NULL) i_fatal("Support not compiled in for %s", handler->name); /* write the compressed output file */ fd_in = open(in_path, O_RDONLY); if (fd_in == -1) i_fatal("open(%s) failed: %m", in_path); fd_out = open(out_path, O_TRUNC | O_CREAT | O_RDWR, 0600); if (fd_out == -1) i_fatal("creat(%s) failed: %m", out_path); sha1_init(&sha1); file_output = o_stream_create_fd_file(fd_out, 0, FALSE); output = handler->create_ostream(file_output, 1); input = i_stream_create_fd_autoclose(&fd_in, IO_BLOCK_SIZE); while (i_stream_read_data(input, &data, &size, 0) > 0) { sha1_loop(&sha1, data, size); o_stream_nsend(output, data, size); i_stream_skip(input, size); } if (o_stream_nfinish(output) < 0) { i_fatal("write(%s) failed: %s", out_path, o_stream_get_error(output)); } i_stream_destroy(&input); o_stream_destroy(&output); o_stream_destroy(&file_output); sha1_result(&sha1, output_sha1); /* verify that we can read the compressed file */ sha1_init(&sha1); file_input = i_stream_create_fd(fd_out, IO_BLOCK_SIZE, FALSE); input = handler->create_istream(file_input, FALSE); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { sha1_loop(&sha1, data, size); i_stream_skip(input, size); } i_stream_destroy(&input); i_stream_destroy(&file_input); sha1_result(&sha1, input_sha1); if (memcmp(input_sha1, output_sha1, sizeof(input_sha1)) != 0) i_fatal("Decompression couldn't get the original input"); i_close_fd(&fd_out); }
static int copy_to_temp_file(struct seekable_istream *sstream) { struct istream_private *stream = &sstream->istream; const char *path; const unsigned char *buffer; size_t size; int fd; fd = sstream->fd_callback(&path, sstream->context); if (fd == -1) return -1; /* copy our currently read buffer to it */ i_assert(stream->pos <= sstream->buffer_peak); if (write_full(fd, stream->buffer, sstream->buffer_peak) < 0) { if (!ENOSPACE(errno)) i_error("istream-seekable: write_full(%s) failed: %m", path); i_close_fd(&fd); return -1; } sstream->temp_path = i_strdup(path); sstream->write_peak = sstream->buffer_peak; sstream->fd = fd; sstream->fd_input = i_stream_create_fd_autoclose(&fd, I_MAX(stream->pos, sstream->istream.max_buffer_size)); i_stream_set_name(sstream->fd_input, t_strdup_printf( "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream))); /* read back the data we just had in our buffer */ for (;;) { buffer = i_stream_get_data(sstream->fd_input, &size); if (size >= stream->pos) break; ssize_t ret; if ((ret = i_stream_read_memarea(sstream->fd_input)) <= 0) { i_assert(ret != 0); i_assert(ret != -2); i_error("istream-seekable: Couldn't read back " "in-memory input %s: %s", i_stream_get_name(&stream->istream), i_stream_get_error(sstream->fd_input)); i_stream_destroy(&sstream->fd_input); i_close_fd(&sstream->fd); return -1; } } /* Set the max buffer size only after we've already read everything into memory. For example with istream-data it's possible that more data exists in buffer than max_buffer_size. */ i_stream_set_max_buffer_size(sstream->fd_input, sstream->istream.max_buffer_size); stream->buffer = buffer; i_stream_free_buffer(&sstream->istream); return 0; }
static const char * file_lock_find_proc_locks(int lock_fd ATTR_UNUSED) { /* do anything except Linux support this? don't bother trying it for OSes we don't know about. */ #ifdef __linux__ static bool have_proc_locks = TRUE; struct stat st; char node_buf[MAX_INT_STRLEN*3 + 2 + 1]; struct istream *input; const char *line, *lock_type = ""; pid_t pid = 0; int fd; if (!have_proc_locks) return FALSE; if (fstat(lock_fd, &st) < 0) return ""; i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu", major(st.st_dev), minor(st.st_dev), (unsigned long long)st.st_ino); fd = open("/proc/locks", O_RDONLY); if (fd == -1) { have_proc_locks = FALSE; return ""; } input = i_stream_create_fd_autoclose(&fd, 512); while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN { const char *const *args = t_strsplit_spaces(line, " "); /* number: FLOCK/POSIX ADVISORY READ/WRITE pid major:minor:inode region-start region-end */ if (str_array_length(args) < 8) continue; if (strcmp(args[5], node_buf) == 0) { lock_type = strcmp(args[3], "READ") == 0 ? "READ" : "WRITE"; if (str_to_pid(args[4], &pid) < 0) pid = 0; } } T_END; i_stream_destroy(&input); if (pid == 0) { /* not found */ return ""; } if (pid == getpid()) return " (BUG: lock is held by our own process)"; return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid); #else return ""; #endif }
static int copy_to_temp_file(struct seekable_istream *sstream) { struct istream_private *stream = &sstream->istream; const char *path; const unsigned char *buffer; size_t size; int fd; fd = sstream->fd_callback(&path, sstream->context); if (fd == -1) return -1; /* copy our currently read buffer to it */ if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) { if (!ENOSPACE(errno)) i_error("istream-seekable: write_full(%s) failed: %m", path); i_close_fd(&fd); return -1; } sstream->temp_path = i_strdup(path); sstream->write_peak = sstream->membuf->used; sstream->fd = fd; sstream->fd_input = i_stream_create_fd_autoclose(&fd, sstream->istream.max_buffer_size); i_stream_set_name(sstream->fd_input, t_strdup_printf( "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream))); /* read back the data we just had in our buffer */ i_stream_seek(sstream->fd_input, stream->istream.v_offset); for (;;) { buffer = i_stream_get_data(sstream->fd_input, &size); if (size >= stream->pos) break; if (i_stream_read(sstream->fd_input) <= 0) { i_error("istream-seekable: Couldn't read back " "in-memory input %s", i_stream_get_name(&stream->istream)); i_stream_destroy(&sstream->fd_input); return -1; } } stream->buffer = buffer; stream->pos = size; buffer_free(&sstream->membuf); return 0; }
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; }
static void stats_dump(const char *path, const char *cmd) { struct istream *input; const char *const *args; unsigned int i; int fd; fd = doveadm_connect(path); net_set_nonblock(fd, FALSE); if (write_full(fd, cmd, strlen(cmd)) < 0) i_fatal("write(%s) failed: %m", path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); /* read header */ args = read_next_line(input); if (args == NULL) i_fatal("read(%s) unexpectedly disconnected", path); if (*args == NULL) i_info("no statistics available"); else { for (; *args != NULL; args++) doveadm_print_header_simple(*args); /* read lines */ do { T_BEGIN { args = read_next_line(input); if (args != NULL && args[0] == NULL) args = NULL; if (args != NULL) { for (i = 0; args[i] != NULL; i++) doveadm_print(args[i]); } } T_END; } while (args != NULL); } if (input->stream_errno != 0) i_fatal("read(%s) failed: %s", path, i_stream_get_error(input)); i_stream_destroy(&input); }
static struct istream * maildir_open_mail(struct maildir_mailbox *mbox, struct mail *mail, bool *deleted_r) { struct istream *input; const char *path; struct maildir_open_context ctx; *deleted_r = FALSE; ctx.fd = -1; ctx.path = NULL; mail->transaction->stats.open_lookup_count++; if (!mail->saving) { if (maildir_file_do(mbox, mail->uid, do_open, &ctx) < 0) return NULL; } else { path = maildir_save_file_get_path(mail->transaction, mail->seq); if (do_open(mbox, path, &ctx) <= 0) return NULL; } if (ctx.fd == -1) { *deleted_r = TRUE; return NULL; } input = i_stream_create_fd_autoclose(&ctx.fd, 0); if (input->stream_errno == EISDIR) { i_stream_destroy(&input); if (maildir_lose_unexpected_dir(&mbox->storage->storage, ctx.path) >= 0) *deleted_r = TRUE; } else { i_stream_set_name(input, ctx.path); index_mail_set_read_buffer_size(mail, input); } i_free(ctx.path); return input; }
static void cmd_dump_dbox(int argc ATTR_UNUSED, char *argv[]) { struct istream *input; int fd; unsigned int hdr_size; bool ret; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_name(input, argv[1]); hdr_size = dump_file_hdr(input); do { printf("\n"); T_BEGIN { ret = dump_msg(input, hdr_size); } T_END; } while (ret); i_stream_destroy(&input); }
static void test_ostream_file_send_istream_sendfile(void) { struct istream *input, *input2; struct ostream *output; char buf[10]; int fd, sock_fd[2]; test_begin("ostream file send istream sendfile()"); /* temp file istream */ fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.istream) failed: %m"); test_assert(write(fd, "abcdefghij", 10) == 10); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* temp socket ostream */ i_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == 0); output = o_stream_create_fd_autoclose(sock_fd, 0); /* test that sendfile() works */ i_stream_seek(input, 3); input2 = i_stream_create_limit(input, 4); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 4); test_assert(read(sock_fd[1], buf, sizeof(buf)) == 4 && memcmp(buf, "defg", 4) == 0); i_stream_unref(&input2); i_stream_unref(&input); o_stream_destroy(&output); i_close_fd(&sock_fd[1]); i_unlink(".temp.istream"); test_end(); }
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 void test_iostream_temp_istream(void) { struct istream *input, *input2, *temp_input; struct ostream *output; int fd; test_begin("iostream_temp istream"); fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("create(.temp.istream) failed: %m"); test_assert(write(fd, "foobar", 6) == 6); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* a working fd-dup */ output = iostream_temp_create_sized(".nonexistent/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 1); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); temp_input = iostream_temp_finish(&output, 128); test_assert(i_stream_read(temp_input) == 6); i_stream_destroy(&temp_input); /* non-working fd-dup: write data before sending istream */ i_stream_seek(input, 0); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send(output, "1234", 4) == 4); test_assert(output->offset == 4); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 10); test_expect_no_more_errors(); o_stream_destroy(&output); /* non-working fd-dup: write data after sending istream */ i_stream_seek(input, 0); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send(output, "1", 1) == 1); test_assert(output->offset == 7); test_expect_no_more_errors(); o_stream_destroy(&output); /* non-working fd-dup: send two istreams */ i_stream_seek(input, 0); input2 = i_stream_create_limit(input, (uoff_t)-1); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 12); test_expect_no_more_errors(); o_stream_destroy(&output); i_stream_unref(&input2); i_stream_destroy(&input); i_unlink(".temp.istream"); test_end(); }
static void test_ostream_file_send_istream_file(void) { struct istream *input, *input2; struct ostream *output; char buf[10]; int fd; test_begin("ostream file send istream file"); /* temp file istream */ fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.istream) failed: %m"); test_assert(write(fd, "1234567890", 10) == 10); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* temp file ostream */ fd = open(".temp.ostream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.ostream) failed: %m"); output = o_stream_create_fd(fd, 0); /* test that writing works between two files */ i_stream_seek(input, 3); input2 = i_stream_create_limit(input, 4); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 4); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4567", 4) == 0); i_stream_unref(&input2); /* test that writing works within the same file */ i_stream_destroy(&input); input = i_stream_create_fd(fd, 1024); /* forwards: 4567 -> 4677 */ o_stream_seek(output, 1); i_stream_seek(input, 2); input2 = i_stream_create_limit(input, 2); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 3); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4677", 4) == 0); i_stream_destroy(&input2); i_stream_destroy(&input); /* backwards: 1234 -> 11234 */ memcpy(buf, "1234", 4); test_assert(pwrite(fd, buf, 4, 0) == 4); input = i_stream_create_fd(fd, 1024); o_stream_seek(output, 1); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 5); test_assert(pread(fd, buf, sizeof(buf), 0) == 5 && memcmp(buf, "11234", 5) == 0); i_stream_destroy(&input); o_stream_destroy(&output); i_close_fd(&fd); i_unlink(".temp.istream"); i_unlink(".temp.ostream"); test_end(); }
if (file->fd == -1) { T_BEGIN { ret = dbox_file_open_fd(file, try_altpath); } T_END; if (ret <= 0) { if (ret < 0) return -1; *notfound_r = TRUE; return 1; } } /* we're manually checking at dbox_file_close() if we need to close the fd or not. */ fd = file->fd; file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE); i_stream_set_name(file->input, file->cur_path); i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE); return dbox_file_read_header(file); } int dbox_file_open(struct dbox_file *file, bool *deleted_r) { return dbox_file_open_full(file, TRUE, deleted_r); } int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r) { return dbox_file_open_full(file, FALSE, notfound_r); }
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); }