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);


--
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

svn-commits mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/svn-commits

Reply via email to