[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