Example #1
0
static void
maildir_mail_remove_sizes_from_filename(struct mail *mail,
					enum mail_fetch_field field)
{
	struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box;
	enum maildir_uidlist_rec_flag flags;
	const char *fname;
	uoff_t size;
	char wrong_key;

	if (mbox->storage->set->maildir_broken_filename_sizes) {
		/* never try to fix sizes in maildir filenames */
		return;
	}

	if (maildir_sync_lookup(mbox, mail->uid, &flags, &fname) <= 0)
		return;
	if (strchr(fname, MAILDIR_EXTRA_SEP) == NULL)
		return;

	if (field == MAIL_FETCH_VIRTUAL_SIZE &&
	    maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE,
				      &size)) {
		wrong_key = 'W';
	} else if (field == MAIL_FETCH_PHYSICAL_SIZE &&
		   maildir_filename_get_size(fname, MAILDIR_EXTRA_FILE_SIZE,
					     &size)) {
		wrong_key = 'S';
	} else {
		/* the broken size isn't in filename */
		return;
	}

	(void)maildir_file_do(mbox, mail->uid, do_fix_size, &wrong_key);
}
Example #2
0
static void
maildir_mail_remove_sizes_from_filename(struct mail *mail,
					enum mail_fetch_field field)
{
	struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box;
	struct mail_private *pmail = (struct mail_private *)mail;
	enum maildir_uidlist_rec_flag flags;
	const char *fname;
	uoff_t size;
	struct maildir_size_fix_ctx ctx;

	if (mbox->storage->set->maildir_broken_filename_sizes) {
		/* never try to fix sizes in maildir filenames */
		return;
	}

	if (maildir_sync_lookup(mbox, mail->uid, &flags, &fname) <= 0)
		return;
	if (strchr(fname, MAILDIR_EXTRA_SEP) == NULL)
		return;

	memset(&ctx, 0, sizeof(ctx));
	ctx.physical_size = (uoff_t)-1;
	if (field == MAIL_FETCH_VIRTUAL_SIZE &&
	    maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE,
				      &size)) {
		ctx.wrong_key = 'W';
	} else if (field == MAIL_FETCH_PHYSICAL_SIZE &&
		   maildir_filename_get_size(fname, MAILDIR_EXTRA_FILE_SIZE,
					     &size)) {
		ctx.wrong_key = 'S';
	} else {
		/* the broken size isn't in filename */
		return;
	}

	if (pmail->v.istream_opened != NULL) {
		/* the mail could be e.g. compressed. get the physical size
		   the slow way by actually reading the mail. */
		struct istream *input;
		const struct stat *stp;

		if (mail_get_stream(mail, NULL, NULL, &input) < 0)
			return;
		if (i_stream_stat(input, TRUE, &stp) < 0)
			return;
		ctx.physical_size = stp->st_size;
	}

	(void)maildir_file_do(mbox, mail->uid, do_fix_size, &ctx);
}
Example #3
0
static int maildir_quick_size_lookup(struct index_mail *mail, bool vsize,
				     uoff_t *size_r)
{
	struct mail *_mail = &mail->mail.mail;
	struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
	enum maildir_uidlist_rec_ext_key key;
	const char *path, *fname, *value;

	if (!_mail->saving) {
		if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
			return -1;
	} else {
		if (maildir_save_file_get_size(_mail->transaction, _mail->seq,
					       vsize, size_r) == 0)
			return 1;

		path = maildir_save_file_get_path(_mail->transaction,
						  _mail->seq);
		fname = strrchr(path, '/');
		fname = fname != NULL ? fname + 1 : path;
	}

	/* size can be included in filename */
	if (vsize || !mbox->storage->set->maildir_broken_filename_sizes) {
		if (maildir_filename_get_size(fname,
				vsize ? MAILDIR_EXTRA_VIRTUAL_SIZE :
					MAILDIR_EXTRA_FILE_SIZE, size_r))
			return 1;
	}

	/* size can be included in uidlist entry */
	if (!_mail->saving) {
		key = vsize ? MAILDIR_UIDLIST_REC_EXT_VSIZE :
			MAILDIR_UIDLIST_REC_EXT_PSIZE;
		value = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
						   key);
		if (value != NULL && str_to_uoff(value, size_r) == 0)
			return 1;
	}
	return 0;
}
Example #4
0
static int maildir_fix_duplicate(struct maildir_sync_context *ctx,
				 const char *dir, const char *fname2)
{
	const char *fname1, *path1, *path2;
	const char *new_fname, *new_path;
	struct stat st1, st2;
	uoff_t size;

	fname1 = maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
							fname2);
	i_assert(fname1 != NULL);

	path1 = t_strconcat(dir, "/", fname1, NULL);
	path2 = t_strconcat(dir, "/", fname2, NULL);

	if (stat(path1, &st1) < 0 || stat(path2, &st2) < 0) {
		/* most likely the files just don't exist anymore.
		   don't really care about other errors much. */
		return 0;
	}
	if (st1.st_ino == st2.st_ino &&
	    CMP_DEV_T(st1.st_dev, st2.st_dev)) {
		/* Files are the same. this means either a race condition
		   between stat() calls, or that the files were link()ed. */
		if (st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink &&
		    st1.st_ctime == st2.st_ctime &&
		    st1.st_ctime < ioloop_time - DUPE_LINKS_DELETE_SECS) {
			/* The file has hard links and it hasn't had any
			   changes (such as renames) for a while, so this
			   isn't a race condition.

			   rename()ing one file on top of the other would fix
			   this safely, except POSIX decided that rename()
			   doesn't work that way. So we'll have unlink() one
			   and hope that another process didn't just decide to
			   unlink() the other (uidlist lock prevents this from
			   happening) */
			if (i_unlink(path2) == 0)
				i_warning("Unlinked a duplicate: %s", path2);
		}
		return 0;
	}

	new_fname = maildir_filename_generate();
	/* preserve S= and W= sizes if they're available.
	   (S=size is required for zlib plugin to work) */
	if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_FILE_SIZE, &size)) {
		new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
			new_fname, MAILDIR_EXTRA_FILE_SIZE, size);
	}
	if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_VIRTUAL_SIZE, &size)) {
		new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
			new_fname, MAILDIR_EXTRA_VIRTUAL_SIZE, size);
	}
	new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box),
			       "/new/", new_fname, NULL);

	if (rename(path2, new_path) == 0)
		i_warning("Fixed a duplicate: %s -> %s", path2, new_fname);
	else if (errno != ENOENT) {
		mail_storage_set_critical(&ctx->mbox->storage->storage,
			"Couldn't fix a duplicate: rename(%s, %s) failed: %m",
			path2, new_path);
		return -1;
	}
	return 0;
}
Example #5
0
static int maildir_save_finish_real(struct mail_save_context *_ctx)
{
	struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
	struct mail_storage *storage = &ctx->mbox->storage->storage;
	const char *path;
	off_t real_size;
	uoff_t size;
	int output_errno;

	ctx->last_save_finished = TRUE;
	if (ctx->failed && ctx->fd == -1) {
		/* tmp file creation failed */
		return -1;
	}

	path = t_strconcat(ctx->tmpdir, "/", ctx->file_last->tmp_name, NULL);
	if (!ctx->failed && o_stream_nfinish(_ctx->data.output) < 0) {
		if (!mail_storage_set_error_from_errno(storage)) {
			mail_storage_set_critical(storage,
				"write(%s) failed: %m", path);
		}
		ctx->failed = TRUE;
	}

	if (_ctx->data.save_date != (time_t)-1) {
		/* we can't change ctime, but we can add the date to cache */
		struct index_mail *mail = (struct index_mail *)_ctx->dest_mail;
		uint32_t t = _ctx->data.save_date;

		index_mail_cache_add(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t));
	}

 	if (maildir_save_finish_received_date(ctx, path) < 0)
		ctx->failed = TRUE;

	if (ctx->cur_dest_mail != NULL) {
		index_mail_cache_parse_deinit(ctx->cur_dest_mail,
					      ctx->ctx.data.received_date,
					      !ctx->failed);
	}
	i_stream_unref(&ctx->input);

	/* remember the size in case we want to add it to filename */
	ctx->file_last->size = _ctx->data.output->offset;
	if (ctx->cur_dest_mail == NULL ||
	    mail_get_virtual_size(ctx->cur_dest_mail,
				  &ctx->file_last->vsize) < 0)
		ctx->file_last->vsize = (uoff_t)-1;

	output_errno = _ctx->data.output->last_failed_errno;
	o_stream_destroy(&_ctx->data.output);

	if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER &&
	    !ctx->failed) {
		if (fsync(ctx->fd) < 0) {
			if (!mail_storage_set_error_from_errno(storage)) {
				mail_storage_set_critical(storage,
						  "fsync(%s) failed: %m", path);
			}
			ctx->failed = TRUE;
		}
	}
	real_size = lseek(ctx->fd, 0, SEEK_END);
	if (real_size == (off_t)-1) {
		mail_storage_set_critical(storage,
					  "lseek(%s) failed: %m", path);
	} else if (real_size != (off_t)ctx->file_last->size &&
		   (!maildir_filename_get_size(ctx->file_last->dest_basename,
					       MAILDIR_EXTRA_FILE_SIZE, &size) ||
		    size != ctx->file_last->size)) {
		/* e.g. zlib plugin was used. the "physical size" must be in
		   the maildir filename, since stat() will return wrong size */
		ctx->file_last->preserve_filename = FALSE;
		/* preserve the GUID if needed */
		if (ctx->file_last->guid == NULL)
			ctx->file_last->guid = ctx->file_last->dest_basename;
		/* reset the base name as well, just in case there's a
		   ,W=vsize */
		ctx->file_last->dest_basename = ctx->file_last->tmp_name;
	}
	if (close(ctx->fd) < 0) {
		if (!mail_storage_set_error_from_errno(storage)) {
			mail_storage_set_critical(storage,
						  "close(%s) failed: %m", path);
		}
		ctx->failed = TRUE;
	}
	ctx->fd = -1;

	if (ctx->failed) {
		/* delete the tmp file */
		i_unlink_if_exists(path);

		errno = output_errno;
		if (ENOQUOTA(errno)) {
			mail_storage_set_error(storage,
				MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA);
		} else if (errno != 0) {
			mail_storage_set_critical(storage,
				"write(%s) failed: %m", path);
		}

		maildir_save_remove_last_filename(ctx);
		return -1;
	}

	ctx->file_last = NULL;
	return 0;
}