]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
fs-posix: mkdir missing directory if it's changed by FS_METADATA_WRITE_FNAME
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 3 May 2018 12:22:09 +0000 (15:22 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 3 May 2018 13:21:41 +0000 (16:21 +0300)
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

index b999f7d8f74558c9b314d987a8d192304cb88843..990751b1f37092d909109cc720ec1875a3b40ba3 100644 (file)
@@ -465,9 +465,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 &&
@@ -498,10 +520,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",
@@ -515,7 +534,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;