[asterisk-commits] file: branch file/bucket r395655 - in /team/file/bucket: include/asterisk/ ma...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jul 29 09:57:48 CDT 2013


Author: file
Date: Mon Jul 29 09:57:46 2013
New Revision: 395655

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=395655
Log:
Add temporary files, copying, and moving - along with tests.

Modified:
    team/file/bucket/include/asterisk/bucket.h
    team/file/bucket/main/bucket.c
    team/file/bucket/tests/test_bucket.c

Modified: team/file/bucket/include/asterisk/bucket.h
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/include/asterisk/bucket.h?view=diff&rev=395655&r1=395654&r2=395655
==============================================================================
--- team/file/bucket/include/asterisk/bucket.h (original)
+++ team/file/bucket/include/asterisk/bucket.h Mon Jul 29 09:57:46 2013
@@ -251,6 +251,20 @@
 struct ast_bucket_file *ast_bucket_file_alloc(const char *uri);
 
 /*!
+ * \brief Allocate a new temporary local bucket file
+ *
+ * \param uri Complete URI for the bucket file
+ *
+ * \param non-NULL success
+ * \param NULL failure
+ *
+ * \note To persist the temporary file in backend storage you must call ast_bucket_file_create
+ *
+ * \note Path will automatically be filled with a local temporary file that is deleted upon destruction
+ */
+struct ast_bucket_file *ast_bucket_file_temporary_alloc(const char *uri);
+
+/*!
  * \brief Create a new bucket file in backend storage
  *
  * \param file The bucket file
@@ -259,6 +273,41 @@
  * \retval -1 failure
  */
 int ast_bucket_file_create(struct ast_bucket_file *file);
+
+/*!
+ * \brief Copy a bucket file to a new URI
+ *
+ * \param file The source bucket file
+ * \param uri The new URI
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This does not create the new file in backend storage, you must call ast_bucket_file_create
+ * on the resulting file to do so
+ *
+ */
+struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri);
+
+/*!
+ * \brief Move a bucket file to a new URI
+ *
+ * \param file The source bucket file
+ * \param uri The new URI
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This does not create the new file in backend storage, you must call ast_bucket_file_create
+ * on the resulting file to do so
+ *
+ * \note This does not delete the old file in backend storage, you must call ast_bucket_file_delete
+ * on the source file to do so
+ *
+ * \note The source file is left in an undefined state, the only safe operation to call is
+ * ast_bucket_file_delete
+ */
+struct ast_bucket_file *ast_bucket_file_move(struct ast_bucket_file *file, const char *uri);
 
 /*!
  * \brief Update an existing bucket file in backend storage

Modified: team/file/bucket/main/bucket.c
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/main/bucket.c?view=diff&rev=395655&r1=395654&r2=395655
==============================================================================
--- team/file/bucket/main/bucket.c (original)
+++ team/file/bucket/main/bucket.c Mon Jul 29 09:57:46 2013
@@ -38,6 +38,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/strings.h"
 #include "asterisk/json.h"
+#include "asterisk/file.h"
 
 /*! \brief Default scheme for when one is not specified */
 #define DEFAULT_UNSPECIFIED_SCHEME "local"
@@ -526,6 +527,10 @@
 	struct ast_bucket_file *file = obj;
 
 	ao2_cleanup(file->metadata);
+
+	if (!ast_strlen_zero(file->path)) {
+		unlink(file->path);
+	}
 }
 
 /*! \brief Allocator for bucket files */
@@ -588,12 +593,100 @@
 	ast_string_field_set(file, uri, uri);
 	ast_string_field_set(file, scheme, scheme);
 
+	if (!tmpnam(file->path)) {
+		return NULL;
+	}
+
 	return file;
 }
 
 int ast_bucket_file_create(struct ast_bucket_file *file)
 {
 	return ast_sorcery_create(bucket_sorcery, file);
+}
+
+/*! \brief Copy a file, shamelessly taken from file.c */
+static int bucket_copy(const char *infile, const char *outfile)
+{
+	int ifd, ofd, len;
+	char buf[4096];	/* XXX make it lerger. */
+
+	if ((ifd = open(infile, O_RDONLY)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+		return -1;
+	}
+	if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+		close(ifd);
+		return -1;
+	}
+	while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
+		int res;
+		if (len < 0) {
+			ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+			break;
+		}
+		/* XXX handle partial writes */
+		res = write(ofd, buf, len);
+		if (res != len) {
+			ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+			len = -1; /* error marker */
+			break;
+		}
+	}
+	close(ifd);
+	close(ofd);
+	if (len < 0) {
+		unlink(outfile);
+		return -1; /* error */
+	}
+	return 0;	/* success */
+}
+
+struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri)
+{
+	struct ast_bucket_file *copy;
+
+	copy = ast_bucket_file_alloc(uri);
+	if (!copy) {
+		return NULL;
+	}
+
+	ao2_cleanup(copy->metadata);
+	copy->metadata = ao2_container_clone(file->metadata, 0);
+	if (!copy->metadata) {
+		ao2_ref(copy, -1);
+		return NULL;
+	}
+
+	if (bucket_copy(file->path, copy->path)) {
+		ao2_ref(copy, -1);
+		return NULL;
+	}
+
+	return copy;
+}
+
+struct ast_bucket_file *ast_bucket_file_move(struct ast_bucket_file *file, const char *uri)
+{
+	struct ast_bucket_file *moved;
+
+	moved = ast_bucket_file_alloc(uri);
+	if (!moved) {
+		return NULL;
+	}
+
+	if (rename(file->path, moved->path)) {
+		ao2_ref(moved, -1);
+		return NULL;
+	}
+	file->path[0] = '\0';
+
+	ao2_cleanup(moved->metadata);
+	moved->metadata = file->metadata;
+	file->metadata = NULL;
+
+	return moved;
 }
 
 struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri)

Modified: team/file/bucket/tests/test_bucket.c
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/tests/test_bucket.c?view=diff&rev=395655&r1=395654&r2=395655
==============================================================================
--- team/file/bucket/tests/test_bucket.c (original)
+++ team/file/bucket/tests/test_bucket.c Mon Jul 29 09:57:46 2013
@@ -33,11 +33,14 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "")
 
+#include <sys/stat.h>
+
 #include "asterisk/test.h"
 #include "asterisk/module.h"
 #include "asterisk/bucket.h"
 #include "asterisk/logger.h"
 #include "asterisk/json.h"
+#include "asterisk/file.h"
 
 /*! \brief Test state structure for scheme wizards */
 struct bucket_test_state {
@@ -474,6 +477,11 @@
 		return AST_TEST_FAIL;
 	}
 
+	if (ast_strlen_zero(file->path)) {
+		ast_test_status_update(test, "Expected path to temporary file in allocated file but have nothing\n");
+		return AST_TEST_FAIL;
+	}
+
 	if (strcmp(file->uri, "goat:///tmp/bob")) {
 		ast_test_status_update(test, "URI within allocated file is '%s' and should be goat:///tmp/bob\n",
 			file->uri);
@@ -495,6 +503,65 @@
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(bucket_file_temporary_deletion)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+	FILE *temporary;
+	struct stat st;
+	char path[PATH_MAX];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_temporary_deletion";
+		info->category = "/main/bucket/";
+		info->summary = "bucket temporary file deletion unit test";
+		info->description =
+			"Test deletion of temporary bucket files";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("goat:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!stat(file->path, &st)) {
+		ast_test_status_update(test, "Temporary file '%s' existed before creating it\n",
+			file->path);
+		return AST_TEST_FAIL;
+	}
+
+	if (!(temporary = fopen(file->path, "w"))) {
+		ast_test_status_update(test, "Failed to open temporary file '%s'\n",
+			file->path);
+		return AST_TEST_FAIL;
+	}
+
+	fprintf(temporary, "bob");
+	fclose(temporary);
+
+	if (stat(file->path, &st)) {
+		ast_test_status_update(test, "Temporary file '%s' did not exist after creating it\n",
+			file->path);
+		return AST_TEST_FAIL;
+	}
+
+	ast_copy_string(path, file->path, sizeof(path));
+
+	ao2_cleanup(file);
+	file = NULL;
+
+	if (!stat(path, &st)) {
+		ast_test_status_update(test, "Temporary file '%s' continued to exist after destroying it\n",
+			path);
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
 AST_TEST_DEFINE(bucket_file_create)
 {
 	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
@@ -533,6 +600,137 @@
 	if (!ast_bucket_file_create(file)) {
 		ast_test_status_update(test, "Successfully created file with URI '%s' twice\n",
 			file->uri);
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_file_copy)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket_file *, copy, NULL, ao2_cleanup);
+	FILE *temporary;
+	struct stat old, new;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_copy";
+		info->category = "/main/bucket/";
+		info->summary = "bucket file copying unit test";
+		info->description =
+			"Test copying of bucket files";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("goat:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_bucket_file_metadata_set(file, "bob", "joe");
+
+	if (!(temporary = fopen(file->path, "w"))) {
+		ast_test_status_update(test, "Failed to open temporary file '%s'\n", file->path);
+		return AST_TEST_FAIL;
+	}
+
+	fprintf(temporary, "bob");
+	fclose(temporary);
+
+	if (!(copy = ast_bucket_file_copy(file, "goat:///tmp/bob2"))) {
+		ast_test_status_update(test, "Failed to copy file '%s' to goat:///tmp/bob2\n",
+			file->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (stat(file->path, &old)) {
+		ast_test_status_update(test, "Failed to retrieve information on old file '%s'\n", file->path);
+		return AST_TEST_FAIL;
+	}
+
+	if (stat(copy->path, &new)) {
+		ast_test_status_update(test, "Failed to retrieve information on copy file '%s'\n", copy->path);
+		return AST_TEST_FAIL;
+	}
+
+	if (old.st_size != new.st_size) {
+		ast_test_status_update(test, "Copying of underlying temporary file failed\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ao2_container_count(file->metadata) != ao2_container_count(copy->metadata)) {
+		ast_test_status_update(test, "Number of metadata entries does not match original\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_file_move)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket_file *, moved, NULL, ao2_cleanup);
+	FILE *temporary;
+	struct stat old, new;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_move";
+		info->category = "/main/bucket/";
+		info->summary = "bucket file moving unit test";
+		info->description =
+			"Test moving of bucket files";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("goat:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_bucket_file_metadata_set(file, "bob", "joe");
+
+	if (!(temporary = fopen(file->path, "w"))) {
+		ast_test_status_update(test, "Failed to open temporary file '%s'\n", file->path);
+		return AST_TEST_FAIL;
+	}
+
+	fprintf(temporary, "bob");
+	fclose(temporary);
+
+	if (stat(file->path, &old)) {
+		ast_test_status_update(test, "File '%s' does not exist after writing to it\n", file->path);
+		return AST_TEST_FAIL;
+	}
+
+	if (!(moved = ast_bucket_file_move(file, "goat:///tmp/bob2"))) {
+		ast_test_status_update(test, "Failed to move file '%s' to goat:///tmp/bob2\n",
+			file->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (file->metadata) {
+		ast_test_status_update(test, "Old file '%s' still has metadata when it should not\n", file->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (stat(moved->path, &new)) {
+		ast_test_status_update(test, "New file '%s' does not exist after moving\n", moved->path);
+		return AST_TEST_FAIL;
+	}
+
+	if (old.st_size != new.st_size) {
+		ast_test_status_update(test, "Moving of underlying temporary file failed\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!stat(file->path, &old)) {
+		ast_test_status_update(test, "Old file continues to exist after moving\n");
 		return AST_TEST_FAIL;
 	}
 
@@ -888,7 +1086,10 @@
 	AST_TEST_UNREGISTER(bucket_retrieve);
 	AST_TEST_UNREGISTER(bucket_json);
 	AST_TEST_UNREGISTER(bucket_file_alloc);
+	AST_TEST_UNREGISTER(bucket_file_temporary_deletion);
 	AST_TEST_UNREGISTER(bucket_file_create);
+	AST_TEST_UNREGISTER(bucket_file_copy);
+	AST_TEST_UNREGISTER(bucket_file_move);
 	AST_TEST_UNREGISTER(bucket_file_retrieve);
 	AST_TEST_UNREGISTER(bucket_file_update);
 	AST_TEST_UNREGISTER(bucket_file_delete);
@@ -908,7 +1109,10 @@
 	AST_TEST_REGISTER(bucket_retrieve);
 	AST_TEST_REGISTER(bucket_json);
 	AST_TEST_REGISTER(bucket_file_alloc);
+	AST_TEST_REGISTER(bucket_file_temporary_deletion);
 	AST_TEST_REGISTER(bucket_file_create);
+	AST_TEST_REGISTER(bucket_file_copy);
+	AST_TEST_REGISTER(bucket_file_move);
 	AST_TEST_REGISTER(bucket_file_retrieve);
 	AST_TEST_REGISTER(bucket_file_update);
 	AST_TEST_REGISTER(bucket_file_delete);




More information about the asterisk-commits mailing list