Mercurial > dovecot > core-2.2
changeset 15198:ce57bacc3010
lib-fs API cleanups and improvements
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 12 Oct 2012 00:22:19 +0300 |
parents | 2ac184a82b9a |
children | 73916b7be94e |
files | src/lib-fs/fs-api-private.h src/lib-fs/fs-api.c src/lib-fs/fs-api.h src/lib-fs/fs-posix.c src/lib-fs/fs-sis-common.c src/lib-fs/fs-sis-common.h src/lib-fs/fs-sis-queue.c src/lib-fs/fs-sis.c src/lib-storage/index/dbox-common/dbox-storage.c src/lib-storage/index/dbox-single/sdbox-copy.c src/lib-storage/index/dbox-single/sdbox-file.c src/lib-storage/index/index-attachment.c |
diffstat | 12 files changed, 868 insertions(+), 457 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-fs/fs-api-private.h Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-api-private.h Fri Oct 12 00:22:19 2012 +0300 @@ -4,14 +4,29 @@ #include "fs-api.h" struct fs_vfuncs { - int (*init)(const char *args, const struct fs_settings *set, - struct fs **fs_r, const char **error_r); + struct fs *(*alloc)(void); + int (*init)(struct fs *fs, const char *args, + const struct fs_settings *set); void (*deinit)(struct fs *fs); - int (*open)(struct fs *fs, const char *path, enum fs_open_mode mode, - enum fs_open_flags flags, struct fs_file **file_r); - void (*close)(struct fs_file *file); + enum fs_properties (*get_properties)(struct fs *fs); + + struct fs_file *(*file_init)(struct fs *fs, const char *path, + enum fs_open_mode mode, + enum fs_open_flags flags); + void (*file_deinit)(struct fs_file *file); + void (*set_async_callback)(struct fs_file *file, + fs_file_async_callback_t *callback, + void *context); + void (*wait_async)(struct fs *fs); + + void (*set_metadata)(struct fs_file *file, const char *key, + const char *value); + int (*get_metadata)(struct fs_file *file, + const ARRAY_TYPE(fs_metadata) **metadata_r); + + bool (*prefetch)(struct fs_file *file, uoff_t length); ssize_t (*read)(struct fs_file *file, void *buf, size_t size); struct istream *(*read_stream)(struct fs_file *file, size_t max_buffer_size); @@ -23,14 +38,16 @@ int (*lock)(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r); void (*unlock)(struct fs_lock *lock); - int (*fdatasync)(struct fs_file *file); - int (*exists)(struct fs *fs, const char *path); - int (*stat)(struct fs *fs, const char *path, struct stat *st_r); - int (*link)(struct fs *fs, const char *src, const char *dest); - int (*rename)(struct fs *fs, const char *src, const char *dest); - int (*unlink)(struct fs *fs, const char *path); - int (*rmdir)(struct fs *fs, const char *path); + int (*exists)(struct fs_file *file); + int (*stat)(struct fs_file *file, struct stat *st_r); + int (*copy)(struct fs_file *src, struct fs_file *dest); + int (*rename)(struct fs_file *src, struct fs_file *dest); + int (*delete_file)(struct fs_file *file); + + struct fs_iter *(*iter_init)(struct fs *fs, const char *path); + const char *(*iter_next)(struct fs_iter *iter); + int (*iter_deinit)(struct fs_iter *iter); }; struct fs { @@ -53,6 +70,10 @@ struct fs_file *file; }; +struct fs_iter { + struct fs *fs; +}; + extern struct fs fs_class_posix; extern struct fs fs_class_sis; extern struct fs fs_class_sis_queue; @@ -60,4 +81,6 @@ void fs_set_error(struct fs *fs, const char *fmt, ...) ATTR_FORMAT(2, 3); void fs_set_critical(struct fs *fs, const char *fmt, ...) ATTR_FORMAT(2, 3); +void fs_set_error_async(struct fs *fs); + #endif
--- a/src/lib-fs/fs-api.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-api.c Fri Oct 12 00:22:19 2012 +0300 @@ -14,26 +14,24 @@ fs_alloc(const struct fs *fs_class, const char *args, const struct fs_settings *set, struct fs **fs_r, const char **error_r) { - struct fs *fs = NULL; - char *error_dup = NULL; + struct fs *fs; int ret; + fs = fs_class->v.alloc(); + fs->last_error = str_new(default_pool, 64); + T_BEGIN { - const char *error; - - ret = fs_class->v.init(args, set, &fs, &error); - if (ret < 0) - error_dup = i_strdup(error); + ret = fs_class->v.init(fs, args, set); } T_END; if (ret < 0) { /* a bit kludgy way to allow data stack frame usage in normal conditions but still be able to return error message from data stack. */ - *error_r = t_strdup_printf("%s: %s", fs_class->name, error_dup); - i_free(error_dup); + *error_r = t_strdup_printf("%s: %s", fs_class->name, + fs_last_error(fs)); + fs_deinit(&fs); return -1; } - fs->last_error = str_new(default_pool, 64); *fs_r = fs; return 0; } @@ -57,6 +55,7 @@ void fs_deinit(struct fs **_fs) { struct fs *fs = *_fs; + string_t *last_error = fs->last_error; *_fs = NULL; @@ -65,25 +64,25 @@ fs->name, fs->files_open_count); } - str_free(&fs->last_error); fs->v.deinit(fs); + str_free(&last_error); } -int fs_open(struct fs *fs, const char *path, int mode_flags, - struct fs_file **file_r) +struct fs_file *fs_file_init(struct fs *fs, const char *path, int mode_flags) { - int ret; + struct fs_file *file; + + i_assert(path != NULL); T_BEGIN { - ret = fs->v.open(fs, path, mode_flags & FS_OPEN_MODE_MASK, - mode_flags & ~FS_OPEN_MODE_MASK, file_r); + file = fs->v.file_init(fs, path, mode_flags & FS_OPEN_MODE_MASK, + mode_flags & ~FS_OPEN_MODE_MASK); } T_END; - if (ret == 0) - fs->files_open_count++; - return ret; + fs->files_open_count++; + return file; } -void fs_close(struct fs_file **_file) +void fs_file_deinit(struct fs_file **_file) { struct fs_file *file = *_file; @@ -92,7 +91,28 @@ *_file = NULL; file->fs->files_open_count--; - file->fs->v.close(file); + file->fs->v.file_deinit(file); +} + +enum fs_properties fs_get_properties(struct fs *fs) +{ + return fs->v.get_properties(fs); +} + +void fs_set_metadata(struct fs_file *file, const char *key, const char *value) +{ + if (file->fs->v.set_metadata != NULL) + file->fs->v.set_metadata(file, key, value); +} + +int fs_get_metadata(struct fs_file *file, + const ARRAY_TYPE(fs_metadata) **metadata_r) +{ + if (file->fs->v.get_metadata == NULL) { + fs_set_error(file->fs, "Metadata not supported by backend"); + return -1; + } + return file->fs->v.get_metadata(file, metadata_r); } const char *fs_file_path(struct fs_file *file) @@ -112,6 +132,11 @@ return fs_last_error(file->fs); } +bool fs_prefetch(struct fs_file *file, uoff_t length) +{ + return file->fs->v.prefetch(file, length); +} + ssize_t fs_read(struct fs_file *file, void *buf, size_t size) { return file->fs->v.read(file, buf, size); @@ -150,6 +175,22 @@ (void)file->fs->v.write_stream_finish(file, FALSE); } +void fs_file_set_async_callback(struct fs_file *file, + fs_file_async_callback_t *callback, + void *context) +{ + if (file->fs->v.set_async_callback != NULL) + file->fs->v.set_async_callback(file, callback, context); + else + callback(context); +} + +void fs_wait_async(struct fs *fs) +{ + if (fs->v.wait_async != NULL) + fs->v.wait_async(fs); +} + int fs_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r) { return file->fs->v.lock(file, secs, lock_r); @@ -163,39 +204,49 @@ lock->file->fs->v.unlock(lock); } -int fs_fdatasync(struct fs_file *file) +int fs_exists(struct fs_file *file) { - return file->fs->v.fdatasync(file); + return file->fs->v.exists(file); +} + +int fs_stat(struct fs_file *file, struct stat *st_r) +{ + return file->fs->v.stat(file, st_r); } -int fs_exists(struct fs *fs, const char *path) +int fs_copy(struct fs_file *src, struct fs_file *dest) { - return fs->v.exists(fs, path); + i_assert(src->fs == dest->fs); + return src->fs->v.copy(src, dest); } -int fs_stat(struct fs *fs, const char *path, struct stat *st_r) +int fs_rename(struct fs_file *src, struct fs_file *dest) { - return fs->v.stat(fs, path, st_r); + i_assert(src->fs == dest->fs); + return src->fs->v.rename(src, dest); } -int fs_link(struct fs *fs, const char *src, const char *dest) +int fs_delete(struct fs_file *file) { - return fs->v.link(fs, src, dest); + return file->fs->v.delete_file(file); +} + +struct fs_iter *fs_iter_init(struct fs *fs, const char *path) +{ + return fs->v.iter_init(fs, path); } -int fs_rename(struct fs *fs, const char *src, const char *dest) +int fs_iter_deinit(struct fs_iter **_iter) { - return fs->v.rename(fs, src, dest); + struct fs_iter *iter = *_iter; + + *_iter = NULL; + return iter->fs->v.iter_deinit(iter); } -int fs_unlink(struct fs *fs, const char *path) +const char *fs_iter_next(struct fs_iter *iter) { - return fs->v.unlink(fs, path); -} - -int fs_rmdir(struct fs *fs, const char *path) -{ - return fs->v.rmdir(fs, path); + return iter->fs->v.iter_next(iter); } void fs_set_error(struct fs *fs, const char *fmt, ...) @@ -218,3 +269,9 @@ i_error("fs-%s: %s", fs->name, str_c(fs->last_error)); va_end(args); } + +void fs_set_error_async(struct fs *fs) +{ + fs_set_error(fs, "Asynchronous operation in progress"); + errno = EAGAIN; +}
--- a/src/lib-fs/fs-api.h Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-api.h Fri Oct 12 00:22:19 2012 +0300 @@ -6,11 +6,23 @@ struct fs_file; struct fs_lock; +enum fs_properties { + FS_PROPERTY_METADATA = 0x01, + FS_PROPERTY_LOCKS = 0x02, + FS_PROPERTY_FASTCOPY = 0x04, + FS_PROPERTY_RENAME = 0x08, + FS_PROPERTY_STAT = 0x10 +}; + enum fs_open_mode { /* Open only for reading, or fail with ENOENT if it doesn't exist */ - FS_OPEN_MODE_RDONLY, + FS_OPEN_MODE_READONLY, /* Create a new file, fail with EEXIST if it already exists */ FS_OPEN_MODE_CREATE, + /* Create a new file with a new unique name. The generated name is a + 128bit hex-encoded string. The fs_open()'s path parameter specifies + only the directory where the file is created to. */ + FS_OPEN_MODE_CREATE_UNIQUE_128, /* Create or replace a file */ FS_OPEN_MODE_REPLACE, /* Append to existing file, fail with ENOENT if it doesn't exist */ @@ -20,29 +32,58 @@ }; enum fs_open_flags { - /* Call fdatasync() on files after writes */ - FS_OPEN_FLAG_FDATASYNC = 0x10, - /* Create any missing parent directories for new files */ - FS_OPEN_FLAG_MKDIR = 0x20 + /* File being written isn't very important, performance is more + important than actually guaranteeing that the file gets saved */ + FS_OPEN_FLAG_UNIMPORTANT = 0x10, + /* Asynchronous writes: fs_write() will fail with EAGAIN if it needs to + be called again (the retries can use size=0). For streams + fs_write_stream_finish() may request retrying with 0. + + Asynchronous reads: fs_read() will fail with EAGAIN if it's not + finished and fs_read_stream() returns a nonblocking stream. */ + FS_OPEN_FLAG_ASYNC = 0x20 }; struct fs_settings { + /* Dovecot instance's base_dir */ + const char *base_dir; + /* Automatically try to rmdir() directories up to this path when + deleting files. */ + const char *root_path; /* When creating temporary files, use this prefix (to avoid conflicts with existing files). */ const char *temp_file_prefix; }; +struct fs_metadata { + const char *key; + const char *value; +}; +ARRAY_DEFINE_TYPE(fs_metadata, struct fs_metadata); + +typedef void fs_file_async_callback_t(void *context); + int fs_init(const char *driver, const char *args, const struct fs_settings *set, struct fs **fs_r, const char **error_r); void fs_deinit(struct fs **fs); -/* Returns 0 if opened, -1 if error (errno is set). */ -int fs_open(struct fs *fs, const char *path, int mode_flags, - struct fs_file **file_r); -void fs_close(struct fs_file **file); +struct fs_file *fs_file_init(struct fs *fs, const char *path, int mode_flags); +void fs_file_deinit(struct fs_file **file); + +/* Return properties supported by backend. */ +enum fs_properties fs_get_properties(struct fs *fs); -/* Returns the path given to fs_open(). */ +/* Add/replace metadata when saving a file. This makes sense only when the + file is being created/replaced. */ +void fs_set_metadata(struct fs_file *file, const char *key, const char *value); +/* Return file's all metadata. */ +int fs_get_metadata(struct fs_file *file, + const ARRAY_TYPE(fs_metadata) **metadata_r); + +/* Returns the path given to fs_open(). If file was opened with + FS_OPEN_MODE_CREATE_UNIQUE_128 and the write has already finished, + return the path including the generated filename. */ const char *fs_file_path(struct fs_file *file); /* Return the error message for the last failed operation. */ @@ -50,6 +91,11 @@ /* Convenience function for the above. Errors aren't preserved across files. */ const char *fs_file_last_error(struct fs_file *file); +/* Try to asynchronously prefetch file into memory. Returns TRUE if file is + already in memory (i.e. caller should handle this file before prefetching + more), FALSE if not. The length is a hint of how much the caller expects + to read, but it may be more or less (0=whole file). */ +bool fs_prefetch(struct fs_file *file, uoff_t length); /* Returns >0 if something was read, -1 if error (errno is set). */ ssize_t fs_read(struct fs_file *file, void *buf, size_t size); /* Returns a stream for reading from file. Multiple streams can be opened, @@ -68,36 +114,50 @@ struct ostream *fs_write_stream(struct fs_file *file); /* Finish writing via stream. The file will be created/replaced/appended only after this call, same as with fs_write(). Anything written to the stream - won't be visible earlier. */ + won't be visible earlier. Returns 1 if ok, 0 if async write isn't finished + yet (retry calling this), -1 if error */ int fs_write_stream_finish(struct fs_file *file, struct ostream **output); /* Abort writing via stream. Anything written to the stream is discarded. */ void fs_write_stream_abort(struct fs_file *file, struct ostream **output); +/* Call the specified callback whenever the file can be read/written to. + May call the callback immediately. */ +void fs_file_set_async_callback(struct fs_file *file, + fs_file_async_callback_t *callback, + void *context); +/* Wait until some file can be read/written to more before returning. + It's an error to call this when there are no pending async operations. */ +void fs_wait_async(struct fs *fs); + +/* Returns 1 if file exists, 0 if not, -1 if error occurred. */ +int fs_exists(struct fs_file *file); +/* Delete a file. Returns 1 if file was actually deleted by us, + 0 if file didn't exist, -1 if error. */ +int fs_delete(struct fs_file *file); + +/* Returns 0 if ok, -1 if error occurred (e.g. errno=ENOENT). + All fs backends may not support all stat fields. */ +int fs_stat(struct fs_file *file, struct stat *st_r); +/* Copy an object with possibly updated metadata. Destination parent + directories are created automatically. Returns 0 if ok, -1 if error + occurred. */ +int fs_copy(struct fs_file *src, struct fs_file *dest); +/* Atomically rename a file. Destination parent directories are created + automatically. Returns 0 if ok, -1 if error occurred. */ +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 number of seconds (0 = fail immediately). Returns 1 if locked, 0 if wait timed out, -1 if error. */ int fs_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r); void fs_unlock(struct fs_lock **lock); -/* Make sure all written data is flushed to disk. */ -int fs_fdatasync(struct fs_file *file); - -/* Returns 1 if file exists, 0 if not, -1 if error occurred. */ -int fs_exists(struct fs *fs, const char *path); -/* Returns 0 if ok, -1 if error occurred (e.g. errno=ENOENT). - All fs backends may not support all stat fields. */ -int fs_stat(struct fs *fs, const char *path, struct stat *st_r); -/* Create a hard link. Destination parent directories are created - automatically. Returns 0 if ok, -1 if error occurred - (errno=EXDEV if hard links not supported by backend). */ -int fs_link(struct fs *fs, const char *src, const char *dest); -/* Atomically rename a file. Destination parent directories are created - automatically. Returns 0 if ok, -1 if error occurred - (errno=EXDEV if hard links not supported by backend). */ -int fs_rename(struct fs *fs, const char *src, const char *dest); -/* Unlink a file. */ -int fs_unlink(struct fs *fs, const char *path); -/* Delete a directory. Returns 0 if ok, -1 if error occurred. */ -int fs_rmdir(struct fs *fs, const char *path); +/* Iterate through all files (but not directories) in the given directory. + Doesn't recurse to child directories. */ +struct fs_iter *fs_iter_init(struct fs *fs, const char *path); +/* Returns 0 if ok, -1 if iteration failed. */ +int fs_iter_deinit(struct fs_iter **iter); +/* Returns the next filename. */ +const char *fs_iter_next(struct fs_iter *iter); #endif
--- a/src/lib-fs/fs-posix.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-posix.c Fri Oct 12 00:22:19 2012 +0300 @@ -3,6 +3,7 @@ #include "lib.h" #include "buffer.h" #include "str.h" +#include "guid.h" #include "istream.h" #include "ostream.h" #include "safe-mkstemp.h" @@ -15,6 +16,7 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <dirent.h> #include <fcntl.h> #include <sys/stat.h> @@ -29,7 +31,7 @@ struct posix_fs { struct fs fs; - char *temp_file_prefix; + char *temp_file_prefix, *root_path; enum fs_posix_lock_method lock_method; mode_t mode, dir_mode; }; @@ -53,18 +55,33 @@ struct dotlock *dotlock; }; -static int -fs_posix_init(const char *args, const struct fs_settings *set, - struct fs **fs_r, const char **error_r) +struct posix_fs_iter { + struct fs_iter iter; + char *path; + DIR *dir; + int err; +}; + +static struct fs *fs_posix_alloc(void) { struct posix_fs *fs; - const char *const *tmp; fs = i_new(struct posix_fs, 1); fs->fs = fs_class_posix; + return &fs->fs; +} + +static int +fs_posix_init(struct fs *_fs, const char *args, const struct fs_settings *set) +{ + struct posix_fs *fs = (struct posix_fs *)_fs; + const char *const *tmp; + fs->temp_file_prefix = set->temp_file_prefix != NULL ? i_strdup(set->temp_file_prefix) : i_strdup("temp.dovecot."); + fs->root_path = i_strdup(set->root_path); fs->fs.set.temp_file_prefix = fs->temp_file_prefix; + fs->fs.set.root_path = fs->root_path; fs->lock_method = FS_POSIX_LOCK_METHOD_FLOCK; fs->mode = FS_DEFAULT_MODE; @@ -80,12 +97,11 @@ else if (strncmp(arg, "mode=", 5) == 0) { fs->mode = strtoul(arg+5, NULL, 8) & 0666; if (fs->mode == 0) { - *error_r = t_strdup_printf("Invalid mode: %s", - arg+5); + fs_set_error(_fs, "Invalid mode: %s", arg+5); return -1; } } else { - *error_r = t_strdup_printf("Unknown arg '%s'", arg); + fs_set_error(_fs, "Unknown arg '%s'", arg); return -1; } } @@ -93,8 +109,6 @@ if ((fs->dir_mode & 0600) != 0) fs->dir_mode |= 0100; if ((fs->dir_mode & 0060) != 0) fs->dir_mode |= 0010; if ((fs->dir_mode & 0006) != 0) fs->dir_mode |= 0001; - - *fs_r = &fs->fs; return 0; } @@ -103,10 +117,17 @@ struct posix_fs *fs = (struct posix_fs *)_fs; i_free(fs->temp_file_prefix); + i_free(fs->root_path); i_free(fs); } -static int fs_posix_create_parent_dir(struct posix_fs *fs, const char *path) +static enum fs_properties fs_posix_get_properties(struct fs *fs ATTR_UNUSED) +{ + return FS_PROPERTY_LOCKS | FS_PROPERTY_FASTCOPY | FS_PROPERTY_RENAME | + FS_PROPERTY_STAT; +} + +static int fs_posix_mkdir_parents(struct posix_fs *fs, const char *path) { const char *dir, *fname; @@ -124,92 +145,127 @@ } } -static int -fs_posix_create(struct posix_fs *fs, const char *path, enum fs_open_flags flags, - char **temp_path_r) +static int fs_posix_rmdir_parents(struct posix_fs *fs, const char *path) { - struct fs *_fs = &fs->fs; + const char *p; + + if (fs->root_path == NULL) + return 0; + + while ((p = strrchr(path, '/')) != NULL) { + path = t_strdup_until(path, p); + if (strcmp(path, fs->root_path) == 0) + break; + if (rmdir(path) == 0) { + /* success, continue to parent */ + } else if (errno == ENOTEMPTY || errno == EEXIST) { + /* there are other entries in this directory */ + break; + } else if (errno == EBUSY || errno == ENOENT) { + /* some other not-unexpected error */ + break; + } else { + fs_set_error(&fs->fs, "rmdir(%s) failed: %m", path); + return -1; + } + } + return 0; +} + +static int fs_posix_create(struct posix_fs_file *file) +{ + struct posix_fs *fs = (struct posix_fs *)file->file.fs; string_t *str = t_str_new(256); - const char *slash = strrchr(path, '/'); + const char *slash; unsigned int try_count = 0; int fd; - if (slash != NULL) - str_append_n(str, path, slash-path + 1); + i_assert(file->temp_path == NULL); + + if (file->open_mode == FS_OPEN_MODE_CREATE_UNIQUE_128) { + str_append(str, file->file.path); + str_append_c(str, '/'); + } else if ((slash = strrchr(file->file.path, '/')) != NULL) { + str_append_n(str, file->file.path, slash - file->file.path + 1); + } str_append(str, fs->temp_file_prefix); fd = safe_mkstemp_hostpid(str, fs->mode, (uid_t)-1, (gid_t)-1); while (fd == -1 && errno == ENOENT && - try_count <= MAX_MKDIR_RETRY_COUNT && - (flags & FS_OPEN_FLAG_MKDIR) != 0) { - if (fs_posix_create_parent_dir(fs, path) < 0) + try_count <= MAX_MKDIR_RETRY_COUNT) { + if (fs_posix_mkdir_parents(fs, str_c(str)) < 0) return -1; fd = safe_mkstemp_hostpid(str, fs->mode, (uid_t)-1, (gid_t)-1); try_count++; } if (fd == -1) { - fs_set_error(_fs, "safe_mkstemp(%s) failed: %m", str_c(str)); + fs_set_error(&fs->fs, "safe_mkstemp(%s) failed: %m", str_c(str)); return -1; } - *temp_path_r = i_strdup(str_c(str)); + file->temp_path = i_strdup(str_c(str)); return fd; } -static int -fs_posix_open(struct fs *_fs, const char *path, enum fs_open_mode mode, - enum fs_open_flags flags, struct fs_file **file_r) +static int fs_posix_open(struct posix_fs_file *file) { - struct posix_fs *fs = (struct posix_fs *)_fs; - struct posix_fs_file *file; - char *temp_path = NULL; - int fd = -1; + struct posix_fs *fs = (struct posix_fs *)file->file.fs; + const char *path = file->file.path; + + i_assert(file->fd == -1); - switch (mode) { - case FS_OPEN_MODE_RDONLY: - fd = open(path, O_RDONLY); - if (fd == -1) - fs_set_error(_fs, "open(%s) failed: %m", path); + switch (file->open_mode) { + case FS_OPEN_MODE_READONLY: + file->fd = open(path, O_RDONLY); + if (file->fd == -1) + fs_set_error(&fs->fs, "open(%s) failed: %m", path); break; case FS_OPEN_MODE_APPEND: - fd = open(path, O_RDWR | O_APPEND); - if (fd == -1) - fs_set_error(_fs, "open(%s) failed: %m", path); + file->fd = open(path, O_RDWR | O_APPEND); + if (file->fd == -1) + fs_set_error(&fs->fs, "open(%s) failed: %m", path); break; + case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_REPLACE: T_BEGIN { - fd = fs_posix_create(fs, path, flags, &temp_path); + file->fd = fs_posix_create(file); } T_END; break; } - if (fd == -1) + if (file->fd == -1) return -1; + return 0; +} + +static struct fs_file * +fs_posix_file_init(struct fs *_fs, const char *path, + enum fs_open_mode mode, enum fs_open_flags flags) +{ + struct posix_fs_file *file; file = i_new(struct posix_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->open_mode = mode; file->open_flags = flags; - file->temp_path = temp_path; - file->fd = fd; - - *file_r = &file->file; - return 0; + file->fd = -1; + return &file->file; } -static void fs_posix_close(struct fs_file *_file) +static void fs_posix_file_deinit(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; i_assert(_file->output == NULL); switch (file->open_mode) { - case FS_OPEN_MODE_RDONLY: + case FS_OPEN_MODE_READONLY: case FS_OPEN_MODE_APPEND: break; + case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_REPLACE: - if (file->success) + if (file->success || file->temp_path == NULL) break; /* failed to create/replace this. delete the temp file */ if (unlink(file->temp_path) < 0) { @@ -230,11 +286,32 @@ i_free(file); } +static bool fs_posix_prefetch(struct fs_file *_file, uoff_t length) +{ + struct posix_fs_file *file = (struct posix_fs_file *)_file; + + if (file->fd == -1) { + if (fs_posix_open(file) < 0) + return TRUE; + } + + if (posix_fadvise(file->fd, 0, length, POSIX_FADV_WILLNEED) < 0) { + i_error("posix_fadvise(%s) failed: %m", _file->path); + return TRUE; + } + return FALSE; +} + static ssize_t fs_posix_read(struct fs_file *_file, void *buf, size_t size) { struct posix_fs_file *file = (struct posix_fs_file *)_file; ssize_t ret; + if (file->fd == -1) { + if (fs_posix_open(file) < 0) + return -1; + } + if (file->seek_to_beginning) { file->seek_to_beginning = FALSE; if (lseek(file->fd, 0, SEEK_SET) < 0) { @@ -254,7 +331,15 @@ fs_posix_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct posix_fs_file *file = (struct posix_fs_file *)_file; + struct istream *input; + if (file->fd == -1) { + if (fs_posix_open(file) < 0) { + input = i_stream_create_error(errno); + i_stream_set_name(input, _file->path); + return input; + } + } return i_stream_create_fd(file->fd, max_buffer_size, FALSE); } @@ -262,9 +347,12 @@ { int ret; - if ((file->open_flags & FS_OPEN_FLAG_FDATASYNC) != 0) { - if (fs_fdatasync(&file->file) < 0) + if ((file->open_flags & FS_OPEN_FLAG_UNIMPORTANT) == 0) { + if (fdatasync(file->fd) < 0) { + fs_set_error(file->file.fs, "fdatasync(%s) failed: %m", + file->file.path); return -1; + } } if (close(file->fd) < 0) { @@ -276,6 +364,18 @@ file->fd = -1; switch (file->open_mode) { + case FS_OPEN_MODE_CREATE_UNIQUE_128: + T_BEGIN { + guid_128_t guid; + char *path; + + guid_128_generate(guid); + path = i_strdup_printf("%s/%s", file->file.path, + guid_128_to_string(guid)); + i_free(file->file.path); + file->file.path = path; + } T_END; + /* fall through */ case FS_OPEN_MODE_CREATE: if ((ret = link(file->temp_path, file->file.path)) < 0) { fs_set_error(file->file.fs, "link(%s, %s) failed: %m", @@ -298,6 +398,7 @@ default: i_unreached(); } + i_free_and_null(file->temp_path); file->success = TRUE; file->seek_to_beginning = TRUE; return 0; @@ -308,6 +409,11 @@ struct posix_fs_file *file = (struct posix_fs_file *)_file; ssize_t ret; + if (file->fd == -1) { + if (fs_posix_open(file) < 0) + return -1; + } + if (file->open_mode != FS_OPEN_MODE_APPEND) { if (write_full(file->fd, data, size) < 0) { fs_set_error(_file->fs, "write(%s) failed: %m", @@ -342,10 +448,13 @@ if (file->open_mode == FS_OPEN_MODE_APPEND) { file->write_buf = buffer_create_dynamic(default_pool, 1024*32); _file->output = o_stream_create_buffer(file->write_buf); - return; + } else if (file->fd == -1 && fs_posix_open(file) < 0) { + _file->output = o_stream_create_error(errno); + } else { + _file->output = o_stream_create_fd_file(file->fd, + (uoff_t)-1, FALSE); } - - _file->output = o_stream_create_fd_file(file->fd, (uoff_t)-1, FALSE); + o_stream_set_name(_file->output, _file->path); } static int fs_posix_write_stream_finish(struct fs_file *_file, bool success) @@ -369,14 +478,15 @@ buffer_free(&file->write_buf); break; case FS_OPEN_MODE_CREATE: + case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_REPLACE: if (ret == 0) ret = fs_posix_write_finish(file); break; - case FS_OPEN_MODE_RDONLY: + case FS_OPEN_MODE_READONLY: i_unreached(); } - return ret; + return ret < 0 ? -1 : 1; } static int @@ -451,25 +561,14 @@ i_free(lock); } -static int fs_posix_fdatasync(struct fs_file *_file) -{ - struct posix_fs_file *file = (struct posix_fs_file *)_file; - - if (fdatasync(file->fd) < 0) { - fs_set_error(_file->fs, "fdatasync(%s) failed: %m", - _file->path); - return -1; - } - return 0; -} - -static int fs_posix_exists(struct fs *fs, const char *path) +static int fs_posix_exists(struct fs_file *_file) { struct stat st; - if (stat(path, &st) < 0) { + if (stat(_file->path, &st) < 0) { if (errno != ENOENT) { - fs_set_error(fs, "stat(%s) failed: %m", path); + fs_set_error(_file->fs, "stat(%s) failed: %m", + _file->path); return -1; } return 0; @@ -477,82 +576,157 @@ return 1; } -static int fs_posix_stat(struct fs *fs, const char *path, struct stat *st_r) +static int fs_posix_stat(struct fs_file *_file, struct stat *st_r) { - if (stat(path, st_r) < 0) { - fs_set_error(fs, "stat(%s) failed: %m", path); + if (stat(_file->path, st_r) < 0) { + fs_set_error(_file->fs, "stat(%s) failed: %m", _file->path); return -1; } return 0; } -static int fs_posix_link(struct fs *_fs, const char *src, const char *dest) +static int fs_posix_copy(struct fs_file *_src, struct fs_file *_dest) { - struct posix_fs *fs = (struct posix_fs *)_fs; + struct posix_fs *fs = (struct posix_fs *)_src->fs; unsigned int try_count = 0; int ret; - ret = link(src, dest); + ret = link(_src->path, _dest->path); while (ret < 0 && errno == ENOENT && try_count <= MAX_MKDIR_RETRY_COUNT) { - if (fs_posix_create_parent_dir(fs, dest) < 0) + if (fs_posix_mkdir_parents(fs, _dest->path) < 0) return -1; - ret = link(src, dest); + ret = link(_src->path, _dest->path); try_count++; } if (ret < 0) { - fs_set_error(_fs, "link(%s, %s) failed: %m", src, dest); + fs_set_error(_src->fs, "link(%s, %s) failed: %m", + _src->path, _dest->path); + return -1; + } + return 0; +} + +static int fs_posix_rename(struct fs_file *_src, struct fs_file *_dest) +{ + struct posix_fs *fs = (struct posix_fs *)_src->fs; + unsigned int try_count = 0; + int ret; + + ret = rename(_src->path, _dest->path); + while (ret < 0 && errno == ENOENT && + try_count <= MAX_MKDIR_RETRY_COUNT) { + if (fs_posix_mkdir_parents(fs, _dest->path) < 0) + return -1; + ret = rename(_src->path, _dest->path); + try_count++; + } + if (ret < 0) { + fs_set_error(_src->fs, "rename(%s, %s) failed: %m", + _src->path, _dest->path); return -1; } return 0; } -static int fs_posix_rename(struct fs *_fs, const char *src, const char *dest) +static int fs_posix_delete(struct fs_file *_file) { - struct posix_fs *fs = (struct posix_fs *)_fs; - unsigned int try_count = 0; - int ret; + struct posix_fs *fs = (struct posix_fs *)_file->fs; - ret = rename(src, dest); - while (ret < 0 && errno == ENOENT && - try_count <= MAX_MKDIR_RETRY_COUNT) { - if (fs_posix_create_parent_dir(fs, dest) < 0) - return -1; - ret = rename(src, dest); - try_count++; - } - if (ret < 0) { - fs_set_error(_fs, "link(%s, %s) failed: %m", src, dest); + if (unlink(_file->path) < 0) { + fs_set_error(_file->fs, "unlink(%s) failed: %m", _file->path); return -1; } + (void)fs_posix_rmdir_parents(fs, _file->path); return 0; } -static int fs_posix_unlink(struct fs *fs, const char *path) +static struct fs_iter *fs_posix_iter_init(struct fs *fs, const char *path) { - if (unlink(path) < 0) { - fs_set_error(fs, "unlink(%s) failed: %m", path); - return -1; + struct posix_fs_iter *iter; + + iter = i_new(struct posix_fs_iter, 1); + iter->iter.fs = fs; + iter->path = i_strdup(path); + iter->dir = opendir(path); + if (iter->dir == NULL) { + iter->err = errno; + fs_set_error(fs, "opendir(%s) failed: %m", path); } - return 0; + return &iter->iter; +} + +static bool fs_posix_iter_want(const char *dir, const char *fname) +{ + bool ret; + + T_BEGIN { + const char *path = t_strdup_printf("%s/%s", dir, fname); + struct stat st; + + if (stat(path, &st) < 0) + ret = FALSE; + else + ret = !S_ISDIR(st.st_mode); + } T_END; + return ret; } -static int fs_posix_rmdir(struct fs *fs, const char *path) +static const char *fs_posix_iter_next(struct fs_iter *_iter) { - if (rmdir(path) < 0) { - fs_set_error(fs, "rmdir(%s) failed: %m", path); - return -1; + struct posix_fs_iter *iter = (struct posix_fs_iter *)_iter; + struct dirent *d; + + if (iter->dir == NULL) + return NULL; + + errno = 0; + while ((d = readdir(iter->dir)) != NULL) { +#ifdef HAVE_DIRENT_D_TYPE + if (d->d_type != DT_DIR) { + if (d->d_type == DT_UNKNOWN && + fs_posix_iter_want(iter->path, d->d_name)) + return d->d_name; + } +#else + if (fs_posix_iter_want(iter->path, d->d_name)) + return d->d_name; +#endif + errno = 0; } - return 0; + if (errno != 0) { + iter->err = errno; + fs_set_error(_iter->fs, "readdir(%s) failed: %m", iter->path); + } + return NULL; +} + +static int fs_posix_iter_deinit(struct fs_iter *_iter) +{ + struct posix_fs_iter *iter = (struct posix_fs_iter *)_iter; + int ret = 0; + + if (iter->err != 0) { + errno = iter->err; + ret = -1; + } + i_free(iter->path); + i_free(iter); + return ret; } struct fs fs_class_posix = { .name = "posix", .v = { + fs_posix_alloc, fs_posix_init, fs_posix_deinit, - fs_posix_open, - fs_posix_close, + fs_posix_get_properties, + fs_posix_file_init, + fs_posix_file_deinit, + NULL, NULL, + NULL, NULL, + fs_posix_prefetch, fs_posix_read, fs_posix_read_stream, fs_posix_write, @@ -560,12 +734,13 @@ fs_posix_write_stream_finish, fs_posix_lock, fs_posix_unlock, - fs_posix_fdatasync, fs_posix_exists, fs_posix_stat, - fs_posix_link, + fs_posix_copy, fs_posix_rename, - fs_posix_unlink, - fs_posix_rmdir + fs_posix_delete, + fs_posix_iter_init, + fs_posix_iter_next, + fs_posix_iter_deinit } };
--- a/src/lib-fs/fs-sis-common.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-sis-common.c Fri Oct 12 00:22:19 2012 +0300 @@ -30,29 +30,26 @@ return 0; } -void fs_sis_try_unlink_hash_file(struct fs *fs, struct fs *super, - const char *path) +void fs_sis_try_unlink_hash_file(struct fs *sis_fs, struct fs_file *super_file) { + struct fs_file *hash_file; struct stat st1, st2; - const char *dir, *hash, *hash_path, *hash_dir; + const char *dir, *hash, *hash_path; - if (fs_sis_path_parse(fs, path, &dir, &hash) == 0 && - fs_stat(super, path, &st1) == 0 && st1.st_nlink == 2) { + if (fs_sis_path_parse(sis_fs, super_file->path, &dir, &hash) == 0 && + fs_stat(super_file, &st1) == 0 && st1.st_nlink == 2) { /* this may be the last link. if hashes/ file is the same, delete it. */ hash_path = t_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash); - if (fs_stat(super, hash_path, &st2) == 0 && + hash_file = fs_file_init(super_file->fs, hash_path, + FS_OPEN_MODE_READONLY); + if (fs_stat(hash_file, &st2) == 0 && st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev)) { - if (fs_unlink(super, hash_path) < 0) - i_error("%s", fs_last_error(super)); - else { - /* try to rmdir the hashes/ directory */ - hash_dir = t_strdup_printf("%s/"HASH_DIR_NAME, - dir); - (void)fs_rmdir(super, hash_dir); - } + if (fs_delete(hash_file) < 0) + i_error("%s", fs_last_error(hash_file->fs)); } + fs_file_deinit(&hash_file); } }
--- a/src/lib-fs/fs-sis-common.h Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-sis-common.h Fri Oct 12 00:22:19 2012 +0300 @@ -7,8 +7,7 @@ int fs_sis_path_parse(struct fs *fs, const char *path, const char **dir_r, const char **hash_r); -void fs_sis_try_unlink_hash_file(struct fs *fs, struct fs *super, - const char *path); +void fs_sis_try_unlink_hash_file(struct fs *sis_fs, struct fs_file *super_file); #endif
--- a/src/lib-fs/fs-sis-queue.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-sis-queue.c Fri Oct 12 00:22:19 2012 +0300 @@ -3,6 +3,7 @@ #include "lib.h" #include "str.h" #include "istream.h" +#include "ostream.h" #include "fs-sis-common.h" #define QUEUE_DIR_NAME "queue" @@ -15,6 +16,7 @@ struct sis_queue_fs_file { struct fs_file file; + struct sis_queue_fs *fs; struct fs_file *super; }; @@ -30,21 +32,27 @@ fs_sis_queue_copy_error(fs); } -static int -fs_sis_queue_init(const char *args, const struct fs_settings *set, - struct fs **fs_r, const char **error_r) +static struct fs *fs_sis_queue_alloc(void) { struct sis_queue_fs *fs; - const char *p, *parent_name, *parent_args, *error; fs = i_new(struct sis_queue_fs, 1); fs->fs = fs_class_sis_queue; + return &fs->fs; +} + +static int +fs_sis_queue_init(struct fs *_fs, const char *args, + const struct fs_settings *set) +{ + struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + const char *p, *parent_name, *parent_args, *error; /* <queue_dir>:<parent fs>[:<args>] */ p = strchr(args, ':'); if (p == NULL || p[1] == '\0') { - *error_r = "Parent filesystem not given as parameter"; + fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } @@ -57,10 +65,9 @@ else parent_name = t_strdup_until(parent_name, parent_args++); if (fs_init(parent_name, parent_args, set, &fs->super, &error) < 0) { - *error_r = t_strdup_printf("%s: %s", parent_name, error); + fs_set_error(_fs, "%s: %s", parent_name, error); return -1; } - *fs_r = &fs->fs; return 0; } @@ -68,55 +75,88 @@ { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; - fs_deinit(&fs->super); + if (fs->super != NULL) + fs_deinit(&fs->super); i_free(fs->queue_dir); i_free(fs); } -static int -fs_sis_queue_open(struct fs *_fs, const char *path, enum fs_open_mode mode, - enum fs_open_flags flags, struct fs_file **file_r) +static enum fs_properties fs_sis_queue_get_properties(struct fs *_fs) +{ + struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + + return fs_get_properties(fs->super); +} + +static struct fs_file * +fs_sis_queue_file_init(struct fs *_fs, const char *path, + enum fs_open_mode mode, enum fs_open_flags flags) { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; struct sis_queue_fs_file *file; - struct fs_file *super; - - if (mode == FS_OPEN_MODE_APPEND) { - fs_set_error(_fs, "APPEND mode not supported"); - return -1; - } - - if (fs_open(fs->super, path, mode | flags, &super) < 0) { - fs_sis_queue_copy_error(fs); - return -1; - } - - switch (mode) { - case FS_OPEN_MODE_RDONLY: - *file_r = super; - return 0; - case FS_OPEN_MODE_CREATE: - case FS_OPEN_MODE_REPLACE: - break; - case FS_OPEN_MODE_APPEND: - i_unreached(); - } file = i_new(struct sis_queue_fs_file, 1); file->file.fs = _fs; - file->file.path = i_strdup(fs_file_path(super)); - file->super = super; - *file_r = &file->file; - return 0; + file->file.path = i_strdup(path); + file->fs = fs; + + if (mode == FS_OPEN_MODE_APPEND) + fs_set_error(_fs, "APPEND mode not supported"); + else + file->super = fs_file_init(fs->super, path, mode | flags); + return &file->file; } -static void fs_sis_queue_close(struct fs_file *_file) +static void fs_sis_queue_file_deinit(struct fs_file *_file) +{ + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; + + if (file->super != NULL) + fs_file_deinit(&file->super); + i_free(file->file.path); + i_free(file); +} + +static void +fs_sis_queue_set_async_callback(struct fs_file *_file, + fs_file_async_callback_t *callback, + void *context) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; - fs_close(&file->super); - i_free(file->file.path); - i_free(file); + fs_file_set_async_callback(file->super, callback, context); +} + +static void fs_sis_queue_wait_async(struct fs *_fs) +{ + struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + + fs_wait_async(fs->super); +} + +static void +fs_sis_queue_set_metadata(struct fs_file *_file, const char *key, + const char *value) +{ + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; + + fs_set_metadata(file->super, key, value); +} + +static int +fs_sis_queue_get_metadata(struct fs_file *_file, + const ARRAY_TYPE(fs_metadata) **metadata_r) +{ + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; + + return fs_get_metadata(file->super, metadata_r); +} + +static bool fs_sis_queue_prefetch(struct fs_file *_file, uoff_t length) +{ + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; + + return fs_prefetch(file->super, length); } static ssize_t fs_sis_queue_read(struct fs_file *_file, void *buf, size_t size) @@ -151,21 +191,18 @@ fname = path; queue_path = t_strdup_printf("%s/%s", fs->queue_dir, fname); - if (fs_open(fs->super, queue_path, - FS_OPEN_MODE_CREATE | FS_OPEN_FLAG_MKDIR, - &queue_file) < 0) { - i_error("fs-sis-queue: %s", fs_last_error(fs->super)); - return; - } + queue_file = fs_file_init(fs->super, queue_path, FS_OPEN_MODE_CREATE); if (fs_write(queue_file, "", 0) < 0 && errno != EEXIST) i_error("fs-sis-queue: %s", fs_last_error(fs->super)); - fs_close(&queue_file); + fs_file_deinit(&queue_file); } static int fs_sis_queue_write(struct fs_file *_file, const void *data, size_t size) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; + if (file->super == NULL) + return -1; if (fs_write(file->super, data, size) < 0) { fs_sis_queue_file_copy_error(file); return -1; @@ -182,7 +219,11 @@ i_assert(_file->output == NULL); - _file->output = fs_write_stream(file->super); + if (file->super == NULL) + _file->output = o_stream_create_error(EINVAL); + else + _file->output = fs_write_stream(file->super); + o_stream_set_name(_file->output, _file->path); } static int fs_sis_queue_write_stream_finish(struct fs_file *_file, bool success) @@ -190,8 +231,10 @@ struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; if (!success) { - fs_write_stream_abort(file->super, &_file->output); - fs_sis_queue_file_copy_error(file); + if (file->super != NULL) { + fs_write_stream_abort(file->super, &_file->output); + fs_sis_queue_file_copy_error(file); + } return -1; } @@ -202,7 +245,7 @@ T_BEGIN { fs_sis_queue_add(file); } T_END; - return 0; + return 1; } static int @@ -223,95 +266,89 @@ i_unreached(); } -static int fs_sis_queue_fdatasync(struct fs_file *_file) +static int fs_sis_queue_exists(struct fs_file *_file) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; - if (fs_fdatasync(file->super) < 0) { - fs_sis_queue_file_copy_error(file); + if (fs_exists(file->super) < 0) { + fs_sis_queue_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_queue_exists(struct fs *_fs, const char *path) +static int fs_sis_queue_stat(struct fs_file *_file, struct stat *st_r) { - struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; - if (fs_exists(fs->super, path) < 0) { - fs_sis_queue_copy_error(fs); + if (fs_stat(file->super, st_r) < 0) { + fs_sis_queue_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_queue_stat(struct fs *_fs, const char *path, - struct stat *st_r) +static int fs_sis_queue_copy(struct fs_file *_src, struct fs_file *_dest) { - struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + struct sis_queue_fs_file *src = (struct sis_queue_fs_file *)_src; + struct sis_queue_fs_file *dest = (struct sis_queue_fs_file *)_dest; - if (fs_stat(fs->super, path, st_r) < 0) { - fs_sis_queue_copy_error(fs); - return -1; - } - return 0; -} - -static int fs_sis_queue_link(struct fs *_fs, const char *src, const char *dest) -{ - struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; - - if (fs_link(fs->super, src, dest) < 0) { - fs_sis_queue_copy_error(fs); + if (fs_copy(src->super, dest->super) < 0) { + fs_sis_queue_copy_error(src->fs); return -1; } return 0; } static int -fs_sis_queue_rename(struct fs *_fs, const char *src, const char *dest) +fs_sis_queue_rename(struct fs_file *_src, struct fs_file *_dest) { - struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + struct sis_queue_fs_file *src = (struct sis_queue_fs_file *)_src; + struct sis_queue_fs_file *dest = (struct sis_queue_fs_file *)_dest; - if (fs_rename(fs->super, src, dest) < 0) { - fs_sis_queue_copy_error(fs); + if (fs_rename(src->super, dest->super) < 0) { + fs_sis_queue_copy_error(src->fs); return -1; } return 0; } -static int fs_sis_queue_unlink(struct fs *_fs, const char *path) +static int fs_sis_queue_delete(struct fs_file *_file) { - struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; + struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; T_BEGIN { - fs_sis_try_unlink_hash_file(&fs->fs, fs->super, path); + fs_sis_try_unlink_hash_file(_file->fs, file->super); } T_END; - if (fs_unlink(fs->super, path) < 0) { - fs_sis_queue_copy_error(fs); + if (fs_delete(file->super) < 0) { + fs_sis_queue_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_queue_rmdir(struct fs *_fs, const char *path) +static struct fs_iter * +fs_sis_queue_iter_init(struct fs *_fs, const char *path) { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; - if (fs_rmdir(fs->super, path) < 0) { - fs_sis_queue_copy_error(fs); - return -1; - } - return 0; + return fs_iter_init(fs->super, path); } struct fs fs_class_sis_queue = { .name = "sis-queue", .v = { + fs_sis_queue_alloc, fs_sis_queue_init, fs_sis_queue_deinit, - fs_sis_queue_open, - fs_sis_queue_close, + fs_sis_queue_get_properties, + fs_sis_queue_file_init, + fs_sis_queue_file_deinit, + fs_sis_queue_set_async_callback, + fs_sis_queue_wait_async, + fs_sis_queue_set_metadata, + fs_sis_queue_get_metadata, + fs_sis_queue_prefetch, fs_sis_queue_read, fs_sis_queue_read_stream, fs_sis_queue_write, @@ -319,12 +356,13 @@ fs_sis_queue_write_stream_finish, fs_sis_queue_lock, fs_sis_queue_unlock, - fs_sis_queue_fdatasync, fs_sis_queue_exists, fs_sis_queue_stat, - fs_sis_queue_link, + fs_sis_queue_copy, fs_sis_queue_rename, - fs_sis_queue_unlink, - fs_sis_queue_rmdir + fs_sis_queue_delete, + fs_sis_queue_iter_init, + NULL, + NULL } };
--- a/src/lib-fs/fs-sis.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-fs/fs-sis.c Fri Oct 12 00:22:19 2012 +0300 @@ -3,9 +3,13 @@ #include "lib.h" #include "str.h" #include "istream.h" +#include "ostream.h" #include "ostream-cmp.h" #include "fs-sis-common.h" +#define FS_SIS_REQUIRED_PROPS \ + (FS_PROPERTY_FASTCOPY | FS_PROPERTY_STAT) + struct sis_fs { struct fs fs; struct fs *super; @@ -13,6 +17,7 @@ struct sis_fs_file { struct fs_file file; + struct sis_fs *fs; struct fs_file *super; enum fs_open_mode open_mode; @@ -21,6 +26,7 @@ struct ostream *fs_output; char *hash, *hash_path; + bool opened; }; static void fs_sis_copy_error(struct sis_fs *fs) @@ -35,18 +41,24 @@ fs_sis_copy_error(fs); } -static int -fs_sis_init(const char *args, const struct fs_settings *set, - struct fs **fs_r, const char **error_r) +static struct fs *fs_sis_alloc(void) { struct sis_fs *fs; - const char *parent_name, *parent_args, *error; fs = i_new(struct sis_fs, 1); fs->fs = fs_class_sis; + return &fs->fs; +} + +static int +fs_sis_init(struct fs *_fs, const char *args, const struct fs_settings *set) +{ + struct sis_fs *fs = (struct sis_fs *)_fs; + enum fs_properties props; + const char *parent_name, *parent_args, *error; if (*args == '\0') { - *error_r = "Parent filesystem not given as parameter"; + fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } @@ -59,10 +71,15 @@ parent_args++; } if (fs_init(parent_name, parent_args, set, &fs->super, &error) < 0) { - *error_r = t_strdup_printf("%s: %s", parent_name, error); + fs_set_error(_fs, "%s: %s", parent_name, error); return -1; } - *fs_r = &fs->fs; + props = fs_get_properties(fs->super); + if ((props & FS_SIS_REQUIRED_PROPS) != FS_SIS_REQUIRED_PROPS) { + fs_set_error(_fs, "%s backend can't be used with SIS", + parent_name); + return -1; + } return 0; } @@ -70,81 +87,115 @@ { struct sis_fs *fs = (struct sis_fs *)_fs; - fs_deinit(&fs->super); + if (fs->super != NULL) + fs_deinit(&fs->super); i_free(fs); } -static int -fs_sis_open(struct fs *_fs, const char *path, enum fs_open_mode mode, - enum fs_open_flags flags, struct fs_file **file_r) +static enum fs_properties fs_sis_get_properties(struct fs *_fs) +{ + struct sis_fs *fs = (struct sis_fs *)_fs; + + return fs_get_properties(fs->super); +} + +static struct fs_file * +fs_sis_file_init(struct fs *_fs, const char *path, + enum fs_open_mode mode, enum fs_open_flags flags) { struct sis_fs *fs = (struct sis_fs *)_fs; struct sis_fs_file *file; - struct fs_file *super; const char *dir, *hash; - if (mode == FS_OPEN_MODE_APPEND) { - fs_set_error(_fs, "APPEND mode not supported"); - return -1; - } - - if (fs_open(fs->super, path, mode | flags, &super) < 0) { - fs_sis_copy_error(fs); - return -1; - } - - switch (mode) { - case FS_OPEN_MODE_RDONLY: - *file_r = super; - return 0; - case FS_OPEN_MODE_CREATE: - case FS_OPEN_MODE_REPLACE: - break; - case FS_OPEN_MODE_APPEND: - i_unreached(); - } - - if (fs_sis_path_parse(_fs, path, &dir, &hash) < 0) - return -1; - file = i_new(struct sis_fs_file, 1); file->file.fs = _fs; - file->file.path = i_strdup(fs_file_path(super)); - file->super = super; + file->file.path = i_strdup(path); + file->fs = fs; file->open_mode = mode; - file->hash = i_strdup(hash); + if (mode == FS_OPEN_MODE_APPEND) { + fs_set_error(_fs, "APPEND mode not supported"); + return &file->file; + } + + if (fs_sis_path_parse(_fs, path, &dir, &hash) < 0) { + fs_set_error(_fs, "Invalid path"); + return &file->file; + } /* if hashes/<hash> already exists, open it */ file->hash_path = i_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash); - if (fs_open(fs->super, file->hash_path, FS_OPEN_MODE_RDONLY, - &file->hash_file) < 0 && errno != ENOENT) { - i_error("fs-sis: Couldn't open hash file: %s", - fs_last_error(fs->super)); - } - if (file->hash_file != NULL) { - file->hash_input = - fs_read_stream(file->hash_file, IO_BLOCK_SIZE); + file->hash_file = fs_file_init(fs->super, file->hash_path, + FS_OPEN_MODE_READONLY); + + file->hash_input = fs_read_stream(file->hash_file, IO_BLOCK_SIZE); + if (i_stream_read(file->hash_input) == -1) { + /* doesn't exist */ + if (errno != ENOENT) { + i_error("fs-sis: Couldn't read hash file %s: %m", + file->hash_path); + } + i_stream_destroy(&file->hash_input); } - *file_r = &file->file; - return 0; + file->super = fs_file_init(fs->super, path, mode | flags); + return &file->file; } -static void fs_sis_close(struct fs_file *_file) +static void fs_sis_file_deinit(struct fs_file *_file) { struct sis_fs_file *file = (struct sis_fs_file *)_file; - if (file->hash_file != NULL) { + if (file->hash_input != NULL) i_stream_unref(&file->hash_input); - fs_close(&file->hash_file); - } - fs_close(&file->super); + fs_file_deinit(&file->hash_file); + fs_file_deinit(&file->super); i_free(file->hash); i_free(file->hash_path); i_free(file->file.path); i_free(file); } +static void +fs_sis_set_async_callback(struct fs_file *_file, + fs_file_async_callback_t *callback, void *context) +{ + struct sis_fs_file *file = (struct sis_fs_file *)_file; + + fs_file_set_async_callback(file->super, callback, context); +} + +static void fs_sis_wait_async(struct fs *_fs) +{ + struct sis_fs *fs = (struct sis_fs *)_fs; + + fs_wait_async(fs->super); +} + +static void +fs_sis_set_metadata(struct fs_file *_file, const char *key, + const char *value) +{ + struct sis_fs_file *file = (struct sis_fs_file *)_file; + + fs_set_metadata(file->super, key, value); +} + +static int +fs_sis_get_metadata(struct fs_file *_file, + const ARRAY_TYPE(fs_metadata) **metadata_r) +{ + struct sis_fs_file *file = (struct sis_fs_file *)_file; + + return fs_get_metadata(file->super, metadata_r); +} + +static bool fs_sis_prefetch(struct fs_file *_file, uoff_t length) +{ + struct sis_fs_file *file = (struct sis_fs_file *)_file; + + return fs_prefetch(file->super, length); +} + static ssize_t fs_sis_read(struct fs_file *_file, void *buf, size_t size) { struct sis_fs_file *file = (struct sis_fs_file *)_file; @@ -165,7 +216,6 @@ static bool fs_sis_try_link(struct sis_fs_file *file) { - const char *path = fs_file_path(&file->file); const struct stat *st; struct stat st2; @@ -173,21 +223,21 @@ return FALSE; /* we can use the existing file */ - if (fs_link(file->super->fs, file->hash_path, path) < 0) { + if (fs_copy(file->hash_file, file->super) < 0) { if (errno != ENOENT && errno != EMLINK) i_error("fs-sis: %s", fs_last_error(file->super->fs)); /* failed to use link(), continue as if it hadn't been equal */ return FALSE; } - if (fs_stat(file->super->fs, path, &st2) < 0) { + if (fs_stat(file->super, &st2) < 0) { i_error("fs-sis: %s", fs_last_error(file->super->fs)); - if (fs_unlink(file->super->fs, path) < 0) + if (fs_delete(file->super) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } if (st->st_ino != st2.st_ino) { /* the hashes/ file was already replaced with something else */ - if (fs_unlink(file->super->fs, path) < 0) + if (fs_delete(file->super) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } @@ -196,15 +246,16 @@ static void fs_sis_replace_hash_file(struct sis_fs_file *file) { - const char *hash_fname, *path = fs_file_path(&file->file); struct fs *super_fs = file->super->fs; + struct fs_file *temp_file; + const char *hash_fname; string_t *temp_path; int ret; if (file->hash_input == NULL) { /* hash file didn't exist previously. we should be able to create it with link() */ - if (fs_link(super_fs, path, file->hash_path) < 0) { + if (fs_copy(file->super, file->hash_file) < 0) { if (errno == EEXIST) { /* the file was just created. it's probably a duplicate, but it's too much trouble @@ -229,33 +280,41 @@ super_fs->set.temp_file_prefix, hash_fname); /* replace existing hash file atomically */ - ret = fs_link(super_fs, path, str_c(temp_path)); + temp_file = fs_file_init(super_fs, str_c(temp_path), + FS_OPEN_MODE_READONLY); + ret = fs_copy(file->super, temp_file); if (ret < 0 && errno == EEXIST) { /* either someone's racing us or it's a stale file. try to continue. */ - if (fs_unlink(super_fs, str_c(temp_path)) < 0 && + if (fs_delete(temp_file) < 0 && errno != ENOENT) i_error("fs-sis: %s", fs_last_error(super_fs)); - ret = fs_link(super_fs, path, str_c(temp_path)); + ret = fs_copy(file->super, temp_file); } if (ret < 0) { i_error("fs-sis: %s", fs_last_error(super_fs)); + fs_file_deinit(&temp_file); return; } - if (fs_rename(super_fs, str_c(temp_path), file->hash_path) < 0) { + + if (fs_rename(temp_file, file->hash_file) < 0) { if (errno == ENOENT) { /* apparently someone else just renamed it. ignore. */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } - (void)fs_unlink(super_fs, str_c(temp_path)); + (void)fs_delete(temp_file); } + fs_file_deinit(&temp_file); } static int fs_sis_write(struct fs_file *_file, const void *data, size_t size) { struct sis_fs_file *file = (struct sis_fs_file *)_file; + if (file->super == NULL) + return -1; + if (file->hash_input != NULL && stream_cmp_block(file->hash_input, data, size) && i_stream_is_eof(file->hash_input)) { @@ -280,14 +339,19 @@ i_assert(_file->output == NULL); - file->fs_output = fs_write_stream(file->super); - if (file->hash_input == NULL) - _file->output = file->fs_output; + if (file->super == NULL) + _file->output = o_stream_create_error(EINVAL); else { - /* compare if files are equal */ - _file->output = o_stream_create_cmp(file->fs_output, - file->hash_input); + file->fs_output = fs_write_stream(file->super); + if (file->hash_input == NULL) + _file->output = file->fs_output; + else { + /* compare if files are equal */ + _file->output = o_stream_create_cmp(file->fs_output, + file->hash_input); + } } + o_stream_set_name(_file->output, _file->path); } static int fs_sis_write_stream_finish(struct fs_file *_file, bool success) @@ -295,8 +359,10 @@ struct sis_fs_file *file = (struct sis_fs_file *)_file; if (!success) { - fs_write_stream_abort(file->super, &file->fs_output); - fs_sis_file_copy_error(file); + if (file->super != NULL) { + fs_write_stream_abort(file->super, &file->fs_output); + fs_sis_file_copy_error(file); + } return -1; } @@ -305,7 +371,7 @@ i_stream_is_eof(file->hash_input)) { if (fs_sis_try_link(file)) { fs_write_stream_abort(file->super, &file->fs_output); - return 0; + return 1; } } @@ -316,7 +382,7 @@ T_BEGIN { fs_sis_replace_hash_file(file); } T_END; - return 0; + return 1; } static int @@ -336,93 +402,88 @@ i_unreached(); } -static int fs_sis_fdatasync(struct fs_file *_file) +static int fs_sis_exists(struct fs_file *_file) { struct sis_fs_file *file = (struct sis_fs_file *)_file; - if (fs_fdatasync(file->super) < 0) { - fs_sis_file_copy_error(file); + if (fs_exists(file->super) < 0) { + fs_sis_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_exists(struct fs *_fs, const char *path) +static int fs_sis_stat(struct fs_file *_file, struct stat *st_r) { - struct sis_fs *fs = (struct sis_fs *)_fs; + struct sis_fs_file *file = (struct sis_fs_file *)_file; - if (fs_exists(fs->super, path) < 0) { - fs_sis_copy_error(fs); + if (fs_stat(file->super, st_r) < 0) { + fs_sis_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_stat(struct fs *_fs, const char *path, struct stat *st_r) +static int fs_sis_copy(struct fs_file *_src, struct fs_file *_dest) { - struct sis_fs *fs = (struct sis_fs *)_fs; + struct sis_fs_file *src = (struct sis_fs_file *)_src; + struct sis_fs_file *dest = (struct sis_fs_file *)_dest; - if (fs_stat(fs->super, path, st_r) < 0) { - fs_sis_copy_error(fs); - return -1; - } - return 0; -} - -static int fs_sis_link(struct fs *_fs, const char *src, const char *dest) -{ - struct sis_fs *fs = (struct sis_fs *)_fs; - - if (fs_link(fs->super, src, dest) < 0) { - fs_sis_copy_error(fs); + if (fs_copy(src->super, dest->super) < 0) { + fs_sis_copy_error(src->fs); return -1; } return 0; } -static int fs_sis_rename(struct fs *_fs, const char *src, const char *dest) +static int fs_sis_rename(struct fs_file *_src, struct fs_file *_dest) { - struct sis_fs *fs = (struct sis_fs *)_fs; + struct sis_fs_file *src = (struct sis_fs_file *)_src; + struct sis_fs_file *dest = (struct sis_fs_file *)_dest; - if (fs_rename(fs->super, src, dest) < 0) { - fs_sis_copy_error(fs); + if (fs_rename(src->super, dest->super) < 0) { + fs_sis_copy_error(src->fs); return -1; } return 0; } -static int fs_sis_unlink(struct fs *_fs, const char *path) +static int fs_sis_delete(struct fs_file *_file) { - struct sis_fs *fs = (struct sis_fs *)_fs; + struct sis_fs_file *file = (struct sis_fs_file *)_file; T_BEGIN { - fs_sis_try_unlink_hash_file(&fs->fs, fs->super, path); + fs_sis_try_unlink_hash_file(_file->fs, file->super); } T_END; - if (fs_unlink(fs->super, path) < 0) { - fs_sis_copy_error(fs); + if (fs_delete(file->super) < 0) { + fs_sis_copy_error(file->fs); return -1; } return 0; } -static int fs_sis_rmdir(struct fs *_fs, const char *path) +static struct fs_iter * +fs_sis_iter_init(struct fs *_fs, const char *path) { struct sis_fs *fs = (struct sis_fs *)_fs; - if (fs_rmdir(fs->super, path) < 0) { - fs_sis_copy_error(fs); - return -1; - } - return 0; + return fs_iter_init(fs->super, path); } struct fs fs_class_sis = { .name = "sis", .v = { + fs_sis_alloc, fs_sis_init, fs_sis_deinit, - fs_sis_open, - fs_sis_close, + fs_sis_get_properties, + fs_sis_file_init, + fs_sis_file_deinit, + fs_sis_set_async_callback, + fs_sis_wait_async, + fs_sis_set_metadata, + fs_sis_get_metadata, + fs_sis_prefetch, fs_sis_read, fs_sis_read_stream, fs_sis_write, @@ -430,12 +491,13 @@ fs_sis_write_stream_finish, fs_sis_lock, fs_sis_unlock, - fs_sis_fdatasync, fs_sis_exists, fs_sis_stat, - fs_sis_link, + fs_sis_copy, fs_sis_rename, - fs_sis_unlink, - fs_sis_rmdir + fs_sis_delete, + fs_sis_iter_init, + NULL, + NULL } };
--- a/src/lib-storage/index/dbox-common/dbox-storage.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-storage/index/dbox-common/dbox-storage.c Fri Oct 12 00:22:19 2012 +0300 @@ -102,6 +102,7 @@ dir = mail_user_home_expand(_storage->user, set->mail_attachment_dir); storage->attachment_dir = p_strdup(_storage->pool, dir); + fs_set.root_path = storage->attachment_dir; if (fs_init(name, args, &fs_set, &storage->attachment_fs, &error) < 0) { *error_r = t_strdup_printf("mail_attachment_fs: %s",
--- a/src/lib-storage/index/dbox-single/sdbox-copy.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-storage/index/dbox-single/sdbox-copy.c Fri Oct 12 00:22:19 2012 +0300 @@ -15,6 +15,7 @@ { struct dbox_storage *src_storage = src_file->file.storage; struct dbox_storage *dest_storage = dest_file->file.storage; + struct fs_file *src_fsfile, *dest_fsfile; ARRAY_TYPE(mail_attachment_extref) extrefs; const struct mail_attachment_extref *extref; const char *extrefs_line, *src, *dest, *dest_relpath; @@ -61,7 +62,11 @@ guid_generate(), NULL); dest = t_strdup_printf("%s/%s", dest_storage->attachment_dir, dest_relpath); - if (fs_link(dest_storage->attachment_fs, src, dest) < 0) { + src_fsfile = fs_file_init(src_storage->attachment_fs, src, + FS_OPEN_MODE_READONLY); + dest_fsfile = fs_file_init(dest_storage->attachment_fs, dest, + FS_OPEN_MODE_READONLY); + if (fs_copy(src_fsfile, dest_fsfile) < 0) { mail_storage_set_critical(&dest_storage->storage, "%s", fs_last_error(dest_storage->attachment_fs)); ret = -1; @@ -69,6 +74,8 @@ array_append(&dest_file->attachment_paths, &dest_relpath, 1); } + fs_file_deinit(&src_fsfile); + fs_file_deinit(&dest_fsfile); } T_END; pool_unref(&pool); return ret;
--- a/src/lib-storage/index/dbox-single/sdbox-file.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-storage/index/dbox-single/sdbox-file.c Fri Oct 12 00:22:19 2012 +0300 @@ -126,6 +126,7 @@ static int sdbox_file_rename_attachments(struct sdbox_file *file) { struct dbox_storage *storage = file->file.storage; + struct fs_file *src_file, *dest_file; const char *const *pathp, *src, *dest; int ret = 0; @@ -133,11 +134,17 @@ 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) { + src_file = fs_file_init(storage->attachment_fs, src, + FS_OPEN_MODE_READONLY); + dest_file = fs_file_init(storage->attachment_fs, dest, + FS_OPEN_MODE_READONLY); + if (fs_rename(src_file, dest_file) < 0) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(storage->attachment_fs)); ret = -1; } + fs_file_deinit(&src_file); + fs_file_deinit(&dest_file); } T_END; return ret; } @@ -181,6 +188,7 @@ { struct dbox_storage *storage = file->file.storage; struct fs *fs = storage->attachment_fs; + struct fs_file *fs_file; const char *const *pathp, *path; int ret = 0; @@ -191,20 +199,25 @@ 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 && + fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); + if (fs_delete(fs_file) < 0 && errno != ENOENT) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(fs)); ret = -1; } + fs_file_deinit(&fs_file); + path = t_strdup_printf("%s/%s", storage->attachment_dir, sdbox_file_attachment_relpath(file, *pathp)); - if (fs_unlink(fs, path) < 0 && + fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); + if (fs_delete(fs_file) < 0 && errno != ENOENT) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(fs)); ret = -1; } + fs_file_deinit(&fs_file); } T_END; return ret; }
--- a/src/lib-storage/index/index-attachment.c Thu Oct 11 23:01:13 2012 +0300 +++ b/src/lib-storage/index/index-attachment.c Fri Oct 12 00:22:19 2012 +0300 @@ -81,14 +81,14 @@ struct mail_save_attachment *attach = ctx->data.attach; struct mail_storage *storage = ctx->transaction->box->storage; struct mail_attachment_extref *extref; - enum fs_open_flags flags = FS_OPEN_FLAG_MKDIR; + enum fs_open_flags flags = 0; const char *attachment_dir, *path, *digest = info->hash; guid_128_t guid_128; i_assert(attach->cur_file == NULL); - if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) - flags |= FS_OPEN_FLAG_FDATASYNC; + if (storage->set->parsed_fsync_mode == FSYNC_MODE_NEVER) + flags |= FS_OPEN_FLAG_UNIMPORTANT; if (strlen(digest) < 4) { /* make sure we can access first 4 bytes without accessing @@ -102,12 +102,8 @@ digest[0], digest[1], digest[2], digest[3], digest, guid_128_to_string(guid_128)); - if (fs_open(attach->fs, path, - FS_OPEN_MODE_CREATE | flags, &attach->cur_file) < 0) { - mail_storage_set_critical(storage, "%s", - fs_last_error(attach->fs)); - return -1; - } + attach->cur_file = fs_file_init(attach->fs, path, + FS_OPEN_MODE_CREATE | flags); extref = array_append_space(&attach->extrefs); extref->start_offset = info->start_offset; @@ -139,7 +135,7 @@ fs_file_last_error(attach->cur_file)); ret = -1; } - fs_close(&attach->cur_file); + fs_file_deinit(&attach->cur_file); if (ret < 0) { array_delete(&attach->extrefs, @@ -258,32 +254,15 @@ index_attachment_delete_real(struct mail_storage *storage, struct fs *fs, const char *name) { - const char *path, *p, *attachment_dir; + struct fs_file *file; + const char *path; int ret; path = t_strdup_printf("%s/%s", index_attachment_dir_get(storage), name); - if ((ret = fs_unlink(fs, path)) < 0) + file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); + if ((ret = fs_delete(file)) < 0) mail_storage_set_critical(storage, "%s", fs_last_error(fs)); - - /* if the directory is now empty, rmdir it and its parents - until it fails */ - attachment_dir = index_attachment_dir_get(storage); - while ((p = strrchr(path, '/')) != NULL) { - path = t_strdup_until(path, p); - if (strcmp(path, attachment_dir) == 0) - break; - - if (fs_rmdir(fs, path) == 0) { - /* success, continue to parent */ - } else if (errno == ENOTEMPTY || errno == EEXIST) { - /* there are other entries in this directory */ - break; - } else { - mail_storage_set_critical(storage, "%s", - fs_last_error(fs)); - break; - } - } + fs_file_deinit(&file); return ret; }