diff src/lib-storage/index/dbox-single/sdbox-file.c @ 12312:28eaaa23f2c6

lib-storage: Added support for saving mail attachments separately via filesystem API. Currently this works only with sdbox and mdbox backends.
author Timo Sirainen <tss@iki.fi>
date Tue, 19 Oct 2010 18:47:17 +0100
parents 8ec35eb6d7b3
children 099925543768
line wrap: on
line diff
--- a/src/lib-storage/index/dbox-single/sdbox-file.c	Tue Oct 19 18:30:51 2010 +0100
+++ b/src/lib-storage/index/dbox-single/sdbox-file.c	Tue Oct 19 18:47:17 2010 +0100
@@ -6,6 +6,9 @@
 #include "mkdir-parents.h"
 #include "istream.h"
 #include "ostream.h"
+#include "str.h"
+#include "fs-api.h"
+#include "dbox-attachment.h"
 #include "sdbox-storage.h"
 #include "sdbox-file.h"
 
@@ -61,6 +64,84 @@
 	return file;
 }
 
+void sdbox_file_free(struct dbox_file *file)
+{
+	struct sdbox_file *sfile = (struct sdbox_file *)file;
+
+	if (sfile->attachment_pool != NULL)
+		pool_unref(&sfile->attachment_pool);
+	dbox_file_free(file);
+}
+
+int sdbox_file_get_attachments(struct dbox_file *file, const char **extrefs_r)
+{
+	const char *line;
+	bool deleted;
+	int ret;
+
+	*extrefs_r = NULL;
+
+	/* read the metadata */
+	ret = dbox_file_open(file, &deleted);
+	if (ret > 0) {
+		if (deleted)
+			return 0;
+		if ((ret = dbox_file_seek(file, 0)) > 0)
+			ret = dbox_file_metadata_read(file);
+	}
+	if (ret <= 0) {
+		if (ret < 0)
+			return -1;
+		/* corrupted file. we're deleting it anyway. */
+		line = NULL;
+	} else {
+		line = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF);
+	}
+	if (line == NULL) {
+		/* no attachments */
+		return 0;
+	}
+	*extrefs_r = line;
+	return 1;
+}
+
+const char *
+sdbox_file_attachment_relpath(struct sdbox_file *file, const char *srcpath)
+{
+	const char *p;
+
+	p = strchr(srcpath, '-');
+	if (p == NULL) {
+		mail_storage_set_critical(file->mbox->box.storage,
+			"sdbox attachment path in invalid format: %s", srcpath);
+	} else {
+		p = strchr(p+1, '-');
+	}
+	return t_strdup_printf("%s-%s-%u",
+			p == NULL ? srcpath : t_strdup_until(srcpath, p),
+			mail_guid_128_to_string(file->mbox->mailbox_guid),
+			file->uid);
+}
+
+static int sdbox_file_rename_attachments(struct sdbox_file *file)
+{
+	struct dbox_storage *storage = file->file.storage;
+	const char *const *pathp, *src, *dest;
+	int ret = 0;
+
+	array_foreach(&file->attachment_paths, pathp) T_BEGIN {
+		src = t_strdup_printf("%s/%s", storage->attachment_dir, *pathp);
+		dest = t_strdup_printf("%s/%s", storage->attachment_dir,
+				sdbox_file_attachment_relpath(file, *pathp));
+		if (fs_rename(storage->attachment_fs, src, dest) < 0) {
+			mail_storage_set_critical(&storage->storage, "%s",
+				fs_last_error(storage->attachment_fs));
+			ret = -1;
+		}
+	} T_END;
+	return ret;
+}
+
 int sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid)
 {
 	const char *old_path, *new_fname, *new_path;
@@ -80,9 +161,62 @@
 	}
 	sdbox_file_init_paths(file, new_fname);
 	file->uid = uid;
+
+	if (array_is_created(&file->attachment_paths)) {
+		if (sdbox_file_rename_attachments(file) < 0)
+			return -1;
+	}
 	return 0;
 }
 
+static int sdbox_file_unlink_aborted_save_attachments(struct sdbox_file *file)
+{
+	struct dbox_storage *storage = file->file.storage;
+	struct fs *fs = storage->attachment_fs;
+	const char *const *pathp, *path;
+	int ret = 0;
+
+	array_foreach(&file->attachment_paths, pathp) T_BEGIN {
+		/* we don't know if we aborted before renaming this attachment,
+		   so try deleting both source and dest path. the source paths
+		   point to temporary files (not to source messages'
+		   attachment paths), so it's safe to delete them. */
+		path = t_strdup_printf("%s/%s", storage->attachment_dir,
+				       *pathp);
+		if (fs_unlink(fs, path) < 0 &&
+		    errno != ENOENT) {
+			mail_storage_set_critical(&storage->storage, "%s",
+						  fs_last_error(fs));
+			ret = -1;
+		}
+		path = t_strdup_printf("%s/%s", storage->attachment_dir,
+				sdbox_file_attachment_relpath(file, *pathp));
+		if (fs_unlink(fs, path) < 0 &&
+		    errno != ENOENT) {
+			mail_storage_set_critical(&storage->storage, "%s",
+						  fs_last_error(fs));
+			ret = -1;
+		}
+	} T_END;
+	return ret;
+}
+
+int sdbox_file_unlink_aborted_save(struct sdbox_file *file)
+{
+	int ret = 0;
+
+	if (unlink(file->file.cur_path) < 0) {
+		mail_storage_set_critical(file->mbox->box.storage,
+			"unlink(%s) failed: %m", file->file.cur_path);
+		ret = -1;
+	}
+	if (array_is_created(&file->attachment_paths)) {
+		if (sdbox_file_unlink_aborted_save_attachments(file) < 0)
+			ret = -1;
+	}
+	return ret;
+}
+
 int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
 {
 	struct sdbox_file *sfile = (struct sdbox_file *)file;
@@ -238,3 +372,52 @@
 	}
 	return 0;
 }
+
+static int
+sdbox_unlink_attachments(struct sdbox_file *sfile,
+			 const ARRAY_TYPE(mail_attachment_extref) *extrefs)
+{
+	struct dbox_storage *storage = sfile->file.storage;
+	const struct mail_attachment_extref *extref;
+	const char *path;
+	int ret = 0;
+
+	array_foreach(extrefs, extref) T_BEGIN {
+		path = sdbox_file_attachment_relpath(sfile, extref->path);
+		if (index_attachment_delete(&storage->storage,
+					    storage->attachment_fs, path) < 0)
+			ret = -1;
+	} T_END;
+	return ret;
+}
+
+int sdbox_file_unlink_with_attachments(struct sdbox_file *sfile)
+{
+	ARRAY_TYPE(mail_attachment_extref) extrefs;
+	const char *extrefs_line;
+	pool_t pool;
+	int ret;
+
+	ret = sdbox_file_get_attachments(&sfile->file, &extrefs_line);
+	if (ret < 0)
+		return -1;
+	if (ret == 0) {
+		/* no attachments */
+		return dbox_file_unlink(&sfile->file);
+	}
+
+	pool = pool_alloconly_create("sdbox attachments unlink", 1024);
+	p_array_init(&extrefs, pool, 16);
+	if (!dbox_attachment_parse_extref(extrefs_line, pool, &extrefs)) {
+		i_warning("%s: Ignoring corrupted extref: %s",
+			  sfile->file.cur_path, extrefs_line);
+		array_clear(&extrefs);
+	}
+
+	/* try to delete the file first, so if it fails we don't have
+	   missing attachments */
+	if ((ret = dbox_file_unlink(&sfile->file)) >= 0)
+		(void)sdbox_unlink_attachments(sfile, &extrefs);
+	pool_unref(&pool);
+	return ret;
+}