static int maildir_sync_flags(struct maildir_mailbox *mbox, const char *path, struct maildir_index_sync_context *ctx) { struct mailbox *box = &mbox->box; struct stat st; const char *dir, *fname, *newfname, *newpath; enum mail_index_sync_type sync_type; uint8_t flags8; ctx->flag_change_count++; fname = strrchr(path, '/'); i_assert(fname != NULL); fname++; dir = t_strdup_until(path, fname); i_assert(*fname != '\0'); /* get the current flags and keywords */ maildir_filename_flags_get(ctx->keywords_sync_ctx, fname, &ctx->flags, &ctx->keywords); /* apply changes */ flags8 = ctx->flags; index_sync_changes_apply(ctx->sync_changes, NULL, &flags8, &ctx->keywords, &sync_type); ctx->flags = flags8; /* and try renaming with the new name */ newfname = maildir_filename_flags_kw_set(ctx->keywords_sync_ctx, fname, ctx->flags, &ctx->keywords); newpath = t_strconcat(dir, newfname, NULL); if (strcmp(path, newpath) == 0) { /* just make sure that the file still exists. avoid rename() here because it's slow on HFS. */ if (stat(path, &st) < 0) { if (errno == ENOENT) return 0; mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } } else { if (rename(path, newpath) < 0) { if (errno == ENOENT) return 0; if (!ENOSPACE(errno) && errno != EACCES) { mail_storage_set_critical(box->storage, "rename(%s, %s) failed: %m", path, newpath); } return -1; } } if (box->v.sync_notify != NULL) { box->v.sync_notify(box, ctx->uid, index_sync_type_convert(sync_type)); } return 1; }
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; }
void mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function) { i_assert(function != NULL); if (ENOSPACE(errno)) { mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE); } else { const char *toobig_error = errno != EFBIG ? "" : " (process was started with ulimit -f limit)"; mail_storage_set_critical(&mbox->storage->storage, "%s failed with mbox file %s: %m%s", function, mailbox_get_path(&mbox->box), toobig_error); } }
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; }
static ssize_t i_stream_seekable_read(struct istream_private *stream) { struct seekable_istream *sstream = (struct seekable_istream *)stream; const unsigned char *data; size_t size, pos; ssize_t ret; if (sstream->fd == -1) { if (read_from_buffer(sstream, &ret)) return ret; /* copy everything to temp file and use it as the stream */ if (copy_to_temp_file(sstream) < 0) { stream->max_buffer_size = (size_t)-1; if (!read_from_buffer(sstream, &ret)) i_unreached(); return ret; } i_assert(sstream->fd != -1); } stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip); stream->pos -= stream->skip; stream->skip = 0; i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak); if (stream->istream.v_offset + stream->pos == sstream->write_peak) { /* need to read more */ if (sstream->cur_input == NULL || i_stream_get_data_size(sstream->cur_input) == 0) { ret = read_more(sstream); if (ret == -1 || ret == 0) return ret; } /* save to our file */ data = i_stream_get_data(sstream->cur_input, &size); ret = write(sstream->fd, data, size); if (ret <= 0) { if (ret < 0 && !ENOSPACE(errno)) { i_error("istream-seekable: write_full(%s) failed: %m", sstream->temp_path); } if (i_stream_seekable_write_failed(sstream) < 0) return -1; if (!read_from_buffer(sstream, &ret)) i_unreached(); return ret; } i_stream_sync(sstream->fd_input); i_stream_skip(sstream->cur_input, ret); sstream->write_peak += ret; } i_stream_seek(sstream->fd_input, stream->istream.v_offset); ret = i_stream_read_memarea(sstream->fd_input); if (ret <= 0) { stream->istream.eof = sstream->fd_input->eof; stream->istream.stream_errno = sstream->fd_input->stream_errno; } else { ret = -2; } stream->buffer = i_stream_get_data(sstream->fd_input, &pos); stream->pos -= stream->skip; stream->skip = 0; ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret; stream->pos = pos; return ret; }
int file_set_size(int fd, off_t size) { #ifdef HAVE_POSIX_FALLOCATE static bool posix_fallocate_supported = TRUE; #endif char block[IO_BLOCK_SIZE]; off_t offset; ssize_t ret; struct stat st; i_assert(size >= 0); if (fstat(fd, &st) < 0) { i_error("fstat() failed: %m"); return -1; } if (size < st.st_size) { if (ftruncate(fd, size) < 0) { i_error("ftruncate() failed: %m"); return -1; } return 0; } if (size == st.st_size) return 0; #ifdef HAVE_POSIX_FALLOCATE if (posix_fallocate_supported) { int err; err = posix_fallocate(fd, st.st_size, size - st.st_size); if (err == 0) return 0; if (err != EINVAL /* Solaris */ && err != EOPNOTSUPP /* AOX */) { if (!ENOSPACE(err)) i_error("posix_fallocate() failed: %m"); return -1; } /* Not supported by kernel, fallback to writing. */ posix_fallocate_supported = FALSE; } #endif /* start growing the file */ offset = st.st_size; memset(block, 0, I_MIN((ssize_t)sizeof(block), size - offset)); while (offset < size) { ret = pwrite(fd, block, I_MIN((ssize_t)sizeof(block), size - offset), offset); if (ret < 0) { if (!ENOSPACE(errno)) i_error("pwrite() failed: %m"); return -1; } offset += ret; } return 0; }