int sieve_file_storage_save_finish
(struct sieve_storage_save_context *sctx)
{
	struct sieve_file_save_context *fsctx =
		(struct sieve_file_save_context *)sctx;
	struct sieve_storage *storage = sctx->storage;
	int output_errno;

	if ( sctx->failed && fsctx->fd == -1 ) {
		/* tmp file creation failed */
		return -1;
	}

	T_BEGIN {
		output_errno = fsctx->output->stream_errno;
		o_stream_destroy(&fsctx->output);

		if ( fsync(fsctx->fd) < 0 ) {
			sieve_storage_set_critical(storage, "save: "
				"fsync(%s) failed: %m", fsctx->tmp_path);
			sctx->failed = TRUE;
		}
		if ( close(fsctx->fd) < 0 ) {
			sieve_storage_set_critical(storage, "save: "
				"close(%s) failed: %m", fsctx->tmp_path);
			sctx->failed = TRUE;
		}
		fsctx->fd = -1;

		if ( sctx->failed ) {
			/* delete the tmp file */
			if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
				sieve_storage_sys_warning(storage, "save: "
					"unlink(%s) failed: %m", fsctx->tmp_path);
			}

			fsctx->tmp_path = NULL;
			
			errno = output_errno;
			if ( ENOQUOTA(errno) ) {
				sieve_storage_set_error(storage,
					SIEVE_ERROR_NO_QUOTA,
					"Not enough disk quota");
			} else if ( errno != 0 ) {
				sieve_storage_set_critical(storage, "save: "
					"write(%s) failed: %m", fsctx->tmp_path);
			}
		}
	} T_END;

	return ( sctx->failed ? -1 : 0 );
}
void sieve_file_storage_save_cancel(struct sieve_storage_save_context *sctx)
{
	struct sieve_file_save_context *fsctx =
		(struct sieve_file_save_context *)sctx;
	struct sieve_storage *storage = sctx->storage;

	if (fsctx->tmp_path != NULL &&
		unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
		sieve_storage_sys_warning(storage, "save: "
			"unlink(%s) failed: %m", fsctx->tmp_path);
	}

	i_assert(fsctx->output == NULL);
}
static int sieve_file_storage_script_move
(struct sieve_file_save_context *fsctx, const char *dst)
{
	struct sieve_storage_save_context *sctx = &fsctx->context;
	struct sieve_storage *storage = sctx->storage;
	int result = 0;

	T_BEGIN {

		/* Using rename() to ensure existing files are replaced
		 * without conflicts with other processes using the same
		 * file. The kernel wont fully delete the original until
		 * all processes have closed the file.
		 */
		if (rename(fsctx->tmp_path, dst) == 0)
			result = 0;
		else {
			result = -1;
			if ( ENOQUOTA(errno) ) {
				sieve_storage_set_error(storage,
					SIEVE_ERROR_NO_QUOTA,
					"Not enough disk quota");
			} else if ( errno == EACCES ) {
				sieve_storage_set_critical(storage, "save: "
					"Failed to save Sieve script: "
					"%s", eacces_error_get("rename", dst));
			} else {
				sieve_storage_set_critical(storage, "save: "
					"rename(%s, %s) failed: %m", fsctx->tmp_path, dst);
			}
		}

		/* Always destroy temp file */
		if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
			sieve_storage_sys_warning(storage, "save: "
				"unlink(%s) failed: %m", fsctx->tmp_path);
		}
	} T_END;

	return result;
}
int sieve_file_storage_quota_havespace
(struct sieve_storage *storage, const char *scriptname, size_t size,
	enum sieve_storage_quota *quota_r, uint64_t *limit_r)
{
	struct sieve_file_storage *fstorage =
		(struct sieve_file_storage *)storage;
	struct dirent *dp;
	DIR *dirp;
	uint64_t script_count = 1;
	uint64_t script_storage = size;
	int result = 1;

	/* Open the directory */
	if ( (dirp = opendir(fstorage->path)) == NULL ) {
		sieve_storage_set_critical(storage,
			"quota: opendir(%s) failed: %m", fstorage->path);
		return -1;
	}

	/* Scan all files */
	for (;;) {
		const char *name;
		bool replaced = FALSE;

		/* Read next entry */
		errno = 0;
		if ( (dp = readdir(dirp)) == NULL ) {
			if ( errno != 0 ) {
				sieve_storage_set_critical(storage,
					"quota: readdir(%s) failed: %m", fstorage->path);
				result = -1;
			}
			break;
		}

		/* Parse filename */
		name = sieve_script_file_get_scriptname(dp->d_name);

		/* Ignore non-script files */
		if ( name == NULL )
			continue;

		/* Don't list our active sieve script link if the link
		 * resides in the script dir (generally a bad idea).
		 */
		i_assert( fstorage->link_path != NULL );
		if ( *(fstorage->link_path) == '\0' &&
			strcmp(fstorage->active_fname, dp->d_name) == 0 )
			continue;

		if ( strcmp(name, scriptname) == 0 )
			replaced = TRUE;

		/* Check count quota if necessary */
		if ( storage->max_scripts > 0 ) {
			if ( !replaced ) {
				script_count++;

				if ( script_count > storage->max_scripts ) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSCRIPTS;
					*limit_r = storage->max_scripts;
					result = 0;
					break;
				}
			}
		}

		/* Check storage quota if necessary */
		if ( storage->max_storage > 0 ) {
			const char *path;
			struct stat st;

			path = t_strconcat(fstorage->path, "/", dp->d_name, NULL);

			if ( stat(path, &st) < 0 ) {
				sieve_storage_sys_warning(storage,
					"quota: stat(%s) failed: %m", path);
				continue;
			}

			if ( !replaced ) {
				script_storage += st.st_size;

				if ( script_storage > storage->max_storage ) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSTORAGE;
					*limit_r = storage->max_storage;
					result = 0;
					break;
				}
			}
		}
	}

	/* Close directory */
	if ( closedir(dirp) < 0 ) {
		sieve_storage_set_critical(storage,
			"quota: closedir(%s) failed: %m", fstorage->path);
	}
	return result;
}