i_free(fs->username);
i_free(fs->session_id);
i_free(fs->temp_path_prefix);
- i_free(fs->last_error);
for (i = 0; i < FS_OP_COUNT; i++) {
if (fs->stats.timings[i] != NULL)
stats_dist_deinit(&fs->stats.timings[i]);
fs_file_deinit(&file->parent);
event_unref(&file->event);
pool_unref(&file->metadata_pool);
+ i_free(file->last_error);
}
void fs_file_close(struct fs_file *file)
return file->event;
}
+static struct fs_file *fs_file_get_error_file(struct fs_file *file)
+{
+ /* the error is always kept in the parentmost file */
+ while (file->parent != NULL)
+ file = file->parent;
+ return file;
+}
+
static void ATTR_FORMAT(2, 0)
fs_set_verror(struct event *event, const char *fmt, va_list args)
{
struct event *fs_event = event;
- struct fs *fs;
+ struct fs_file *file;
+ struct fs_iter *iter;
/* NOTE: the event might be a passthrough event. We must log it exactly
once so it gets freed. */
- while ((fs = event_get_ptr(fs_event, FS_EVENT_FIELD_FS)) == NULL) {
+ /* figure out if the error is for a file or iter */
+ while ((file = event_get_ptr(fs_event, FS_EVENT_FIELD_FILE)) == NULL &&
+ (iter = event_get_ptr(fs_event, FS_EVENT_FIELD_ITER)) == NULL) {
fs_event = event_get_parent(fs_event);
i_assert(fs_event != NULL);
}
- /* the error is always kept in the parentmost fs */
- while (fs->parent != NULL)
- fs = fs->parent;
- char *old_error = fs->last_error;
- fs->last_error = i_strdup_vprintf(fmt, args);
- i_free(old_error);
-
- e_debug(event, "%s", fs->last_error);
-}
+ char *new_error = i_strdup_vprintf(fmt, args);
+ e_debug(event, "%s", new_error);
-const char *fs_last_error(struct fs *fs)
-{
- /* the error is always kept in the parentmost fs */
- if (fs->parent != NULL)
- return fs_last_error(fs->parent);
+ /* free old error after strdup in case args point to the old error */
+ if (file != NULL) {
+ file = fs_file_get_error_file(file);
- if (fs->last_error == NULL)
- return "BUG: Unknown fs error";
- return fs->last_error;
+ i_free(file->last_error);
+ file->last_error = new_error;
+ } else {
+ i_assert(iter != NULL);
+ /* Preserve the first error for iters. That's the first
+ thing that went wrong and broke the iteration. */
+ if (iter->last_error == NULL)
+ iter->last_error = new_error;
+ else
+ i_free(new_error);
+ }
}
const char *fs_file_last_error(struct fs_file *file)
{
- return fs_last_error(file->fs);
+ struct fs_file *error_file = fs_file_get_error_file(file);
+
+ if (error_file->last_error == NULL)
+ return "BUG: Unknown file error";
+ return error_file->last_error;
}
bool fs_prefetch(struct fs_file *file, uoff_t length)
ret = iter->fs->v.iter_deinit(iter);
} T_END;
if (ret < 0)
- *error_r = fs_last_error(fs);
+ *error_r = t_strdup(iter->last_error);
+ i_free(iter->last_error);
i_free(iter);
event_unref(&event);
return ret;
/* Returns the file's event. */
struct event *fs_file_event(struct fs_file *file);
-/* Return the error message for the last failed operation. */
-const char *fs_last_error(struct fs *fs);
-/* Convenience function for the above. Errors aren't preserved across files. */
+/* Return the error message for the last failed file operation. Each file
+ keeps track of its own errors. For failed copy/rename operations the "dest"
+ file contains the error. */
const char *fs_file_last_error(struct fs_file *file);
/* Try to asynchronously prefetch file into memory. Returns TRUE if file is
int fs_get_nlinks(struct fs_file *file, nlink_t *nlinks_r);
/* Copy an object with possibly updated metadata. Destination parent
directories are created automatically. Returns 0 if ok, -1 if error
- occurred. */
+ occurred. The "dest" file contains the error. */
int fs_copy(struct fs_file *src, struct fs_file *dest);
/* Try to finish asynchronous fs_copy(). Returns the same as fs_copy(). */
int fs_copy_finish_async(struct fs_file *dest);
/* Atomically rename a file. Destination parent directories are created
- automatically. Returns 0 if ok, -1 if error occurred. */
+ automatically. Returns 0 if ok, -1 if error occurred. The "dest" file
+ contains the error. */
int fs_rename(struct fs_file *src, struct fs_file *dest);
/* Exclusively lock a file. If file is already locked, wait for it for given