changeset 22953:fa8ce7d554ab

fs-posix: mkdir missing directory if it's changed by FS_METADATA_WRITE_FNAME The temp file is created to the initial directory. If the directory is changed by FS_METADATA_WRITE_FNAME, the new destination directory didn't necessarily exist. If the link() or rename() fails with ENOENT, try to mkdir the missing directories.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 03 May 2018 15:22:09 +0300
parents 20f8ba61498e
children 39d0d0e8eca2
files src/lib-fs/fs-posix.c
diffstat 1 files changed, 32 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-fs/fs-posix.c	Tue Oct 24 10:27:23 2017 +0300
+++ b/src/lib-fs/fs-posix.c	Thu May 03 15:22:09 2018 +0300
@@ -466,9 +466,31 @@
 		i_strconcat(fs->path_prefix, file->file.path, NULL);
 }
 
+static int fs_posix_write_finish_link(struct posix_fs_file *file)
+{
+	struct posix_fs *fs = (struct posix_fs *)file->file.fs;
+	unsigned int try_count = 0;
+	int ret;
+
+	ret = link(file->temp_path, file->full_path);
+	while (ret < 0 && errno == ENOENT &&
+	       try_count <= MAX_MKDIR_RETRY_COUNT) {
+		if (fs_posix_mkdir_parents(fs, file->full_path) < 0)
+			return -1;
+		ret = link(file->temp_path, file->full_path);
+		try_count++;
+	}
+	if (ret < 0) {
+		fs_set_error(file->file.fs, "link(%s, %s) failed: %m",
+			     file->temp_path, file->full_path);
+	}
+	return ret;
+}
+
 static int fs_posix_write_finish(struct posix_fs_file *file)
 {
 	struct posix_fs *fs = (struct posix_fs *)file->file.fs;
+	unsigned int try_count = 0;
 	int ret, old_errno;
 
 	if ((file->open_flags & FS_OPEN_FLAG_FSYNC) != 0 &&
@@ -499,10 +521,7 @@
 	switch (file->open_mode) {
 	case FS_OPEN_MODE_CREATE_UNIQUE_128:
 	case FS_OPEN_MODE_CREATE:
-		if ((ret = link(file->temp_path, file->full_path)) < 0) {
-			fs_set_error(file->file.fs, "link(%s, %s) failed: %m",
-				     file->temp_path, file->full_path);
-		}
+		ret = fs_posix_write_finish_link(file);
 		old_errno = errno;
 		if (unlink(file->temp_path) < 0) {
 			fs_set_error(file->file.fs, "unlink(%s) failed: %m",
@@ -516,7 +535,15 @@
 		}
 		break;
 	case FS_OPEN_MODE_REPLACE:
-		if (rename(file->temp_path, file->full_path) < 0) {
+		ret = rename(file->temp_path, file->full_path);
+		while (ret < 0 && errno == ENOENT &&
+		       try_count <= MAX_MKDIR_RETRY_COUNT) {
+			if (fs_posix_mkdir_parents(fs, file->full_path) < 0)
+				return -1;
+			ret = rename(file->temp_path, file->full_path);
+			try_count++;
+		}
+		if (ret < 0) {
 			fs_set_error(file->file.fs, "rename(%s, %s) failed: %m",
 				     file->temp_path, file->full_path);
 			return -1;