Skip to content

Commit

Permalink
Use temporary file name when transferring
Browse files Browse the repository at this point in the history
This patch will add an temporary AXL extension to the file while
it is copying.  The extension is removed after the file copy
is complete.  For example, if you're copying to /tmp/file1,
AXL will actually copy to /tmp/file1._AXL, and then rename to
/tmp/file1 at the end of the transfer.

Additionally, a BB API transfer will also encode the transfer
handle number into the extension like:

/tmp/file1._AXL13456

That way we can later recover the transfer handle number.

Fixes: #66
  • Loading branch information
tonyhutter authored and adammoody committed Jul 14, 2020
1 parent c1984e5 commit d738fb7
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 5 deletions.
143 changes: 138 additions & 5 deletions src/axl.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ typedef enum {
AXL_XFER_STATE_CANCELED, /* transfer was AXL_Cancel'd */
} axl_xfer_state_t;

/* Temporary extension added onto files while they're being transferred. */
#define AXL_EXTENSION "._AXL"

/*
=========================================
Global Variables
Expand Down Expand Up @@ -331,6 +334,23 @@ static int path_type(const char *path) {
return PATH_UNKNOWN;
}

/*
* Given a path name to a file ('path'), add on an extra AXL temporary extension
* in the form of: path._AXL[extra]. So if path = '/tmp/file1' and
* extra = "1234", then the new name is /tmp/file1._AXL1234. The new name is
* allocated and returned as a new string (which must be freed).
*
* 'extra' can be optionally used to store additional information about the
* transfer, such as the BB API transfer handle number, or it can be left NULL.
*/
static char * axl_add_extension(const char *path, const char *extra)
{
char *tmp = NULL;
asprintf(&tmp, "%s%s%s", path, AXL_EXTENSION, extra ? extra : "");
return tmp;
}


/*
* Add a file to an existing transfer handle. No directories.
*
Expand All @@ -341,6 +361,8 @@ static int
__AXL_Add (int id, const char* src, const char* dest)
{
kvtree* file_list = NULL;
char *newdest = NULL;
char *extra = NULL;

axl_xfer_t xtype = AXL_XFER_NULL;
axl_xfer_state_t xstate = AXL_XFER_STATE_NULL;
Expand All @@ -365,7 +387,32 @@ __AXL_Add (int id, const char* src, const char* dest)
* STATUS
* SOURCE */
kvtree* src_hash = kvtree_set_kv(file_list, AXL_KEY_FILES, src);
kvtree_util_set_str(src_hash, AXL_KEY_FILE_DEST, dest);

#ifdef HAVE_BBAPI
/*
* Special case: For BB API we includes the transfer handle in the temporary
* file extension. That way, we can later use it to lookup the transfer
* handle for old transfers and cancel them.
*/
if (xtype == AXL_XFER_ASYNC_BBAPI) {
uint64_t thandle;
if (axl_async_get_bbapi_handle(id, &thandle) !=0 ) {
AXL_ERR("Couldn't get BB API transfer handle");
return AXL_FAILURE;
}

asprintf(&extra, "%lu", thandle);
}
#endif

newdest = axl_add_extension(dest, extra);

#ifdef HAVE_BBAPI
free(extra);
#endif

kvtree_util_set_str(src_hash, AXL_KEY_FILE_DEST, newdest);
free(newdest);
kvtree_util_set_int(src_hash, AXL_KEY_STATUS, AXL_STATUS_SOURCE);

/* add file to transfer data structure, depending on its type */
Expand Down Expand Up @@ -610,6 +657,74 @@ int AXL_Dispatch (int id)
return rc;
}

/*
* Given a path with an AXL temporary extension, allocate an return a new
* string with the extension removed. Also, if extra is specified, return
* a pointer to the offset in 'path_with_extension' where the 'extra' field
* is.
*
* Returns the new allocated path string on success, or NULL on error.
*/
static char * axl_remove_extension(char *path_with_extension, char **extra)
{
int i;
size_t size = strlen(path_with_extension);
char *ext = AXL_EXTENSION;
size_t ext_len = sizeof(AXL_EXTENSION) - 1; /* -1 for '\0' */

/* path should at the very least be a one char file name + extension */
if (size < 1 + ext_len) {
return NULL;
}

/*
* Look backwards from the end of the string for the start of the
* extension.
*/
for (i = size - ext_len; i >= 0; i--) {
if (memcmp(&path_with_extension[i], ext, strlen(AXL_EXTENSION)) == 0) {
/* Match! */
if (extra)
*extra = &path_with_extension[i] + ext_len;
return strndup(path_with_extension, i);
}
}
return NULL;
}

/*
* When you do an AXL transfer, it actually transfers to a temporary file
* behind the scenes. It's only after the transfer is finished that the file
* is renamed to its final name.
*
* This function renames all the temporary files to their final names. We
* assume you're calling this after all the transfers have been successfully
* transferred.
*
* TODO: Make the file renames multithreaded
*/
static void axl_rename_files_to_final_names(int id)
{
char *dst;
kvtree_elem *elem = NULL;
char *newdst;
char *extra;

while ((elem = axl_get_next_path(id, elem, NULL, &dst))) {
extra = NULL;
newdst = NULL;
newdst = axl_remove_extension(dst, &extra);
if (!newdst) {
AXL_ERR("Couldn't remove extension, this shouldn't happen");
/* Nothing we can do... */
free(newdst);
continue;
}
rename(dst, newdst);
free(newdst);
}
}

/* Test if a transfer has completed
* Returns AXL_SUCCESS if the transfer has completed */
int AXL_Test (int id)
Expand All @@ -618,6 +733,8 @@ int AXL_Test (int id)
kvtree* file_list = NULL;
axl_xfer_t xtype = AXL_XFER_NULL;
axl_xfer_state_t xstate = AXL_XFER_STATE_NULL;
int rc;

if (axl_get_info(id, &file_list, &xtype, &xstate) != AXL_SUCCESS) {
AXL_ERR("Could not find transfer info for UID %d", id);
return AXL_FAILURE;
Expand All @@ -632,7 +749,8 @@ int AXL_Test (int id)
int status;
kvtree_util_get_int(file_list, AXL_KEY_STATUS, &status);
if (status == AXL_STATUS_DEST) {
return AXL_SUCCESS;
rc = AXL_SUCCESS;
goto end;
} else if (status == AXL_STATUS_ERROR) {
/* we return success since it's done, even on error,
* caller must call wait to determine whether it was successful */
Expand All @@ -642,7 +760,7 @@ int AXL_Test (int id)
return AXL_FAILURE;
} /* else (status == AXL_STATUS_INPROG) send to XFER interfaces */

int rc = AXL_SUCCESS;
rc = AXL_SUCCESS;
switch (xtype) {
case AXL_XFER_SYNC:
rc = axl_sync_test(id);
Expand All @@ -665,6 +783,12 @@ int AXL_Test (int id)
break;
}

end:

if (rc == AXL_SUCCESS) {
axl_rename_files_to_final_names(id);
}

return rc;
}

Expand All @@ -676,6 +800,8 @@ int AXL_Wait (int id)
kvtree* file_list = NULL;
axl_xfer_t xtype = AXL_XFER_NULL;
axl_xfer_state_t xstate = AXL_XFER_STATE_NULL;
int rc;

if (axl_get_info(id, &file_list, &xtype, &xstate) != AXL_SUCCESS) {
AXL_ERR("Could not find transfer info for UID %d", id);
return AXL_FAILURE;
Expand All @@ -694,7 +820,8 @@ int AXL_Wait (int id)

if (status == AXL_STATUS_DEST) {
kvtree_util_set_int(file_list, AXL_KEY_STATE, (int)AXL_XFER_STATE_COMPLETED);
return AXL_SUCCESS;
rc = AXL_SUCCESS;
goto end;
} else if (status == AXL_STATUS_ERROR) {
return AXL_FAILURE;
} else if (status == AXL_STATUS_SOURCE) {
Expand All @@ -703,7 +830,7 @@ int AXL_Wait (int id)
} /* else (status == AXL_STATUS_INPROG) send to XFER interfaces */

/* if not done, call vendor API to wait */
int rc = AXL_SUCCESS;
rc = AXL_SUCCESS;
switch (xtype) {
case AXL_XFER_SYNC:
rc = axl_sync_wait(id);
Expand All @@ -725,6 +852,12 @@ int AXL_Wait (int id)
rc = AXL_FAILURE;
break;
}

end:
if (rc == AXL_SUCCESS) {
axl_rename_files_to_final_names(id);
}

kvtree_util_set_int(file_list, AXL_KEY_STATE, (int)AXL_XFER_STATE_COMPLETED);

/* write data to file if we have one */
Expand Down
24 changes: 24 additions & 0 deletions src/axl_async_bbapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <ctype.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <stdint.h>
#include "axl_internal.h"
#include "axl_async_bbapi.h"
#include "axl_pthread.h"
Expand Down Expand Up @@ -287,6 +288,29 @@ int axl_async_create_bbapi(int id) {
return AXL_FAILURE;
}

/*
* Return the BBTransferHandle_t (which is just a uint64_t) for a given AXL id.
*
* You can only call this function after axl_async_create_bbapi(id) has been
* called.
*
* Returns 0 on success, 1 on error. On success, thandle contains the transfer
* handle value.
*/
int axl_async_get_bbapi_handle(int id, uint64_t *thandle)
{
#ifdef HAVE_BBAPI
kvtree* file_list = kvtree_get_kv_int(axl_file_lists, AXL_KEY_HANDLE_UID, id);

if (kvtree_util_get_unsigned_long(file_list, AXL_BBAPI_KEY_TRANSFERHANDLE,
thandle) != KVTREE_SUCCESS)
return 1;

return 0;
#endif
return AXL_FAILURE;
}

/* Called from AXL_Add
* Adds file source/destination to BBTransferDef */
int axl_async_add_bbapi (int id, const char* source, const char* dest) {
Expand Down
2 changes: 2 additions & 0 deletions src/axl_async_bbapi.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifndef AXL_ASYNC_BBAPI_H
#define AXL_ASYNC_BBAPI_H
#include <stdint.h>

#define AXL_BBAPI_KEY_TRANSFERHANDLE ("BB_TransferHandle")
#define AXL_BBAPI_KEY_TRANSFERDEF ("BB_TransferDef")
Expand All @@ -15,6 +16,7 @@ int axl_async_init_bbapi(void);
int axl_async_finalize_bbapi(void);
int axl_async_create_bbapi(int id);
int axl_async_add_bbapi(int id, const char* source, const char* destination);
int axl_async_get_bbapi_handle(int id, uint64_t *thandle);
int axl_async_start_bbapi(int id);
int axl_async_test_bbapi(int id);
int axl_async_wait_bbapi(int id);
Expand Down
5 changes: 5 additions & 0 deletions src/axl_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "config.h"

#include <zlib.h>
#include <stdarg.h>

#include "kvtree.h"
#include "kvtree_util.h"

Expand Down Expand Up @@ -150,6 +152,9 @@ void axl_free(void* p);
kvtree_elem * axl_get_next_path(int id, kvtree_elem *elem, char **src,
char **dst);

/* Clone of apsrintf(). See the standard asprintf() man page for details */
int asprintf(char** strp, const char* fmt, ...);

/* given a source file, record its current uid/gid, permissions,
* and timestamps, record them in provided kvtree */
int axl_meta_encode(const char* file, kvtree* meta);
Expand Down
60 changes: 60 additions & 0 deletions src/axl_util.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#include <sys/time.h>

#include "axl_internal.h"
Expand Down Expand Up @@ -31,6 +37,60 @@ void axl_free(void* p) {
}
}

/* Clone of apsrintf(). See the standard asprintf() man page for details */
int asprintf(char** strp, const char* fmt, ...)
{
/*
* This code is taken from the vmalloc(3) man page and modified slightly.
*/
int n;
int size = 100; /* Guess we need no more than 100 bytes */
char* p;
char* np;
va_list ap;

p = malloc(size);
if (p == NULL) {
*strp = NULL;
return -ENOMEM;
}

while (1) {
/* Try to print in the allocated space */

va_start(ap, fmt);
n = vsnprintf(p, size, fmt, ap);
va_end(ap);

/* Check error code */

if (n < 0) {
*strp = NULL;
return -1;
}

/* If that worked, return the string */
if (n < size) {
*strp = p;
return n;
}

/* Else try again with more space */

size = n + 1; /* Precisely what is needed */

np = realloc(p, size);
if (np == NULL) {
*strp = NULL;
free(p);
return -ENOMEM;
} else {
p = np;
}
}
}


/*
* This is an helper function to iterate though a file list for a given
* AXL ID. Usage:
Expand Down

0 comments on commit d738fb7

Please sign in to comment.