From 6f364a136e296d0c3950fa977b6dc61a18a42f3a Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Thu, 3 May 2018 15:22:09 +0300 Subject: [PATCH] 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. --- src/lib-fs/fs-posix.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/lib-fs/fs-posix.c b/src/lib-fs/fs-posix.c index a8df6babee..8b916359a1 100644 --- a/src/lib-fs/fs-posix.c +++ b/src/lib-fs/fs-posix.c @@ -472,9 +472,31 @@ static void fs_posix_write_rename_if_needed(struct posix_fs_file *file) 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 && @@ -505,10 +527,7 @@ static int fs_posix_write_finish(struct posix_fs_file *file) 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", @@ -522,7 +541,15 @@ static int fs_posix_write_finish(struct posix_fs_file *file) } 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;