Skip to content

Commit

Permalink
Merge pull request #9 from AppImage/issue-1
Browse files Browse the repository at this point in the history
appimage_read_file_into_buffer_following_symlinks
  • Loading branch information
TheAssassin authored Sep 11, 2018
2 parents efb3c02 + 0f024af commit 705935f
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 31 deletions.
8 changes: 6 additions & 2 deletions include/appimage/appimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ int appimage_register_in_system(const char *path, bool verbose);
int appimage_unregister_in_system(const char *path, bool verbose);

/* Extract a given file from the appimage following the symlinks until a concrete file is found */
void appimage_extract_file_following_symlinks(const char* appimage_file_path, const char* file_path,
const char* target_dir);
void appimage_extract_file_following_symlinks(const char* appimage_file_path, const char* file_path, const char* target_dir);

/* Read a given file from the AppImage into a freshly allocated buffer following symlinks
* Buffer must be free()d after usage
* */
bool appimage_read_file_into_buffer_following_symlinks(const char* appimage_file_path, const char* file_path, char** buffer, unsigned long* buf_size);

/* Create AppImage thumbnail according to
* https://specifications.freedesktop.org/thumbnail-spec/0.8.0/index.html
Expand Down
2 changes: 2 additions & 0 deletions src/libappimage/appimage_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ struct appimage_handler {

void (*extract_file)(struct appimage_handler* handler, void* entry, const char* target);

bool (*read_file_into_new_buffer)(struct appimage_handler* handler, void* entry, char** buffer, unsigned long* buffer_size);

void (*traverse)(struct appimage_handler* handler, traverse_cb command, void* user_data);

void* cache;
Expand Down
50 changes: 47 additions & 3 deletions src/libappimage/libappimage.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ void squash_extract_inode_to_file(sqfs *fs, sqfs_inode *inode, const gchar *dest
while (bytes_already_read < (*inode).xtra.reg.file_size)
{
char buf[bytes_at_a_time];
if (sqfs_read_range(fs, inode, (sqfs_off_t) bytes_already_read, &bytes_at_a_time, buf)) {
if (sqfs_read_range(fs, inode, (sqfs_off_t) bytes_already_read, &bytes_at_a_time, buf) != SQFS_OK) {
#ifdef STANDALONE
fprintf(stderr, "sqfs_read_range error\n");
#endif
Expand Down Expand Up @@ -1950,6 +1950,12 @@ struct extract_appimage_file_command_data {
const char *path;
const char *destination;
};
struct read_appimage_file_into_buffer_command_data {
const char* file_path;
char* out_buffer;
unsigned long out_buf_size;
bool success;
};

void extract_appimage_file_command(void *handler_data, void *entry_data, void *user_data) {
appimage_handler *h = handler_data;
Expand All @@ -1963,6 +1969,25 @@ void extract_appimage_file_command(void *handler_data, void *entry_data, void *u
free(filename);
}

void read_appimage_file_into_buffer_command(void* handler_data, void* entry_data, void* user_data) {
appimage_handler* h = handler_data;
struct archive_entry* entry = entry_data;
struct read_appimage_file_into_buffer_command_data* params = user_data;

if (h->read_file_into_new_buffer == NULL) {
#ifdef STANDALONE
fprintf(stderr, "read_file_into_new_buffer is NULL, go fix that!\n");
#endif
return;
}

char* filename = h->get_file_name(h, entry);
if (strcmp(params->file_path, filename) == 0)
params->success = h->read_file_into_new_buffer(h, entry, &params->out_buffer, &(params->out_buf_size));

free(filename);
}

void extract_appimage_file(appimage_handler *h, const char *path, const char *destination) {
struct extract_appimage_file_command_data data;
data.path = path;
Expand Down Expand Up @@ -2015,11 +2040,30 @@ void appimage_create_thumbnail(const char *appimage_file_path, bool verbose) {

}

void appimage_extract_file_following_symlinks(const gchar* appimage_file_path, const char* file_path,
const char* target_dir) {
void appimage_extract_file_following_symlinks(const gchar* appimage_file_path, const char* file_path, const char* target_dir) {
appimage_handler handler = create_appimage_handler(appimage_file_path);

extract_appimage_file(&handler, file_path, target_dir);

// TODO: free handler?
}

bool appimage_read_file_into_buffer_following_symlinks(const char* appimage_file_path, const char* file_path, char** buffer, unsigned long* buf_size) {
appimage_handler handler = create_appimage_handler(appimage_file_path);

struct read_appimage_file_into_buffer_command_data data;
data.file_path = file_path;
data.out_buffer = NULL;
data.out_buf_size = 0;
data.success = false;

handler.traverse(&handler, &read_appimage_file_into_buffer_command, &data);

*buffer = data.out_buffer;
*buf_size = data.out_buf_size;
return data.success;

// TODO: free handler?
}

void extract_appimage_file_name(void *handler_data, void *entry_data, void *user_data) {
Expand Down
33 changes: 33 additions & 0 deletions src/libappimage/type1.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,44 @@ void type1_extract_file(appimage_handler* handler, void* data, const char* targe
close(f);
}

bool type1_read_file_into_buf(struct appimage_handler* handler, void* data, char** buffer, unsigned long* buf_size) {
(void) data;

struct archive* a = handler->cache;

struct archive_entry* entry = data;

int64_t file_size = archive_entry_size(entry);

char* new_buffer = (char*) malloc(sizeof(char) * file_size);

if (new_buffer == NULL) {
#ifdef STANDALONE
fprintf(stderr, "failed to allocate enough memory for buffer (required: %ul bytes)\n", file_size);
#endif
return false;
}

if (archive_read_data(a, new_buffer, (size_t) file_size) < 0) {
#ifdef STANDALONE
fprintf(stderr, "failed to read data into buffer: %s\n", archive_error_string(a));
#endif
free(new_buffer);
return false;
}

*buffer = new_buffer;
*buf_size = (unsigned long) file_size;
return true;
}

appimage_handler appimage_type_1_create_handler() {
appimage_handler h;
h.traverse = type1_traverse;
h.get_file_name = type1_get_file_name;
h.extract_file = type1_extract_file;
// TODO
h.read_file_into_new_buffer = type1_read_file_into_buf;
h.type = 1;

return h;
Expand Down
105 changes: 79 additions & 26 deletions src/libappimage/type2.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,49 +65,63 @@ void appimage_type2_extract_regular_file(sqfs* fs, sqfs_inode* inode, const char
squash_extract_inode_to_file(fs, inode, target);
}

void appimage_type2_extract_file_following_symlinks(sqfs* fs, sqfs_inode* inode, const char* target) {
if (inode->base.inode_type == SQUASHFS_REG_TYPE)
appimage_type2_extract_regular_file(fs, inode, target);
else if (inode->base.inode_type == SQUASHFS_SYMLINK_TYPE) {
appimage_type2_extract_symlink(fs, inode, target);
} else {
#ifdef STANDALONE
fprintf(stderr, "WARNING: Unable to extract file of type %d", inode->base.inode_type);
#endif
}
}
bool appimage_type2_resolve_symlink(sqfs* fs, sqfs_inode* inode) {
// no need to do anything if the passed inode is not a symlink
if (inode->base.inode_type != SQUASHFS_SYMLINK_TYPE)
return true;

void appimage_type2_extract_symlink(sqfs* fs, sqfs_inode* inode, const char* target) {
// read twice: once to populate size to be able to allocate the right amount of memory, then to populate the buffer
size_t size;
sqfs_readlink(fs, inode, NULL, &size);

char buf[size];
int ret = sqfs_readlink(fs, inode, buf, &size);

if (ret != 0) {
#ifdef STANDALONE
fprintf(stderr, "WARNING: Symlink error.");
#endif
} else {
return false;
}

sqfs_err err = sqfs_inode_get(fs, inode, fs->sb.root_inode);
if (err != SQFS_OK) {
sqfs_err err = sqfs_inode_get(fs, inode, fs->sb.root_inode);
if (err != SQFS_OK) {
#ifdef STANDALONE
fprintf(stderr, "WARNING: Unable to get the root inode. Error: %d", err);
fprintf(stderr, "WARNING: Unable to get the root inode. Error: %d", err);
#endif
}
return false;
}

bool found = false;
err = sqfs_lookup_path(fs, inode, buf, &found);
if (err != SQFS_OK) {
bool found = false;
err = sqfs_lookup_path(fs, inode, buf, &found);
if (err != SQFS_OK) {
#ifdef STANDALONE
fprintf(stderr, "WARNING: There was an error while trying to lookup a symblink. Error: %d", err);
fprintf(stderr, "WARNING: There was an error while trying to lookup a symblink. Error: %d", err);
#endif
}

if (found)
appimage_type2_extract_file_following_symlinks(fs, inode, target);
return false;
}

return true;
}

bool appimage_type2_extract_file_following_symlinks(sqfs* fs, sqfs_inode* inode, const char* target) {
if (!appimage_type2_resolve_symlink(fs, inode)) {
#ifdef STANDALONE
fprintf(stderr, "ERROR: Failed to resolve symlink");
#endif
return false;
}

if (inode->base.inode_type != SQUASHFS_REG_TYPE) {
#ifdef STANDALONE
fprintf(stderr, "WARNING: Unable to extract file of type %d", inode->base.inode_type);
#endif
return false;
}

appimage_type2_extract_regular_file(fs, inode, target);
return true;
}

void type2_traverse(appimage_handler* handler, traverse_cb command, void* command_data) {
appimage_type2_open(handler);
Expand Down Expand Up @@ -156,7 +170,7 @@ void type2_extract_file(appimage_handler* handler, void* data, const char* targe
appimage_type2_extract_file_following_symlinks(fs, &inode, target);
}

char* type2_read_file_into_buf(struct appimage_handler* handler, void* traverse, void* buf) {
bool type2_read_file_into_buf(struct appimage_handler* handler, void* traverse, char** buffer, unsigned long* buf_size) {
sqfs* fs = handler->cache;
sqfs_traverse* trv = traverse;

Expand All @@ -166,13 +180,52 @@ char* type2_read_file_into_buf(struct appimage_handler* handler, void* traverse,
fprintf(stderr, "sqfs_inode_get error\n");
#endif
}

// resolve symlink if possible
if (!appimage_type2_resolve_symlink(fs, &inode)) {
#ifdef STANDALONE
fprintf(stderr, "ERROR: Failed to resolve symlink");
#endif
return false;
}

if (inode.base.inode_type != SQUASHFS_REG_TYPE) {
#ifdef STANDALONE
fprintf(stderr, "WARNING: Unable to extract file of type %d", inode->base.inode_type);
#endif
return false;
}

uint64_t file_size = inode.xtra.reg.file_size;

char* new_buffer = (char*) malloc(sizeof(char) * file_size);

if (new_buffer == NULL) {
#ifdef STANDALONE
fprintf(stderr, "failed to allocate enough memory for buffer (required: %ul bytes)\n", file_size);
#endif
return false;
}

if (sqfs_read_range(fs, &inode, 0, (sqfs_off_t*) &file_size, new_buffer) != SQFS_OK) {
#ifdef STANDALONE
fprintf(stderr, "failed to read data into buffer\n");
#endif
free(new_buffer);
return false;
}

*buffer = new_buffer;
*buf_size = file_size;
return true;
}

appimage_handler appimage_type_2_create_handler() {
appimage_handler h;
h.traverse = type2_traverse;
h.get_file_name = type2_get_file_name;
h.extract_file = type2_extract_file;
h.read_file_into_new_buffer = type2_read_file_into_buf;
h.type = 2;

return h;
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ftw.h>
#include <unistd.h>
#include <xdg-basedir.h>
#include <gtest/gtest.h>


// fixture providing a temporary directory, and a temporary home directory within that directory
Expand Down
49 changes: 49 additions & 0 deletions tests/test_libappimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstdio>
#include <cstring>
#include <fstream>
#include <memory>

#include <glib.h>
#include <glib/gstdio.h>
Expand Down Expand Up @@ -190,6 +191,54 @@ TEST_F(LibAppImageTest, appimage_extract_file_following_symlinks) {
remove(target_path.c_str());
}

TEST_F(LibAppImageTest, appimage_read_file_into_buffer_following_symlinks_type_2) {
char* buf = NULL;
unsigned long bufsize = 0;
bool rv = appimage_read_file_into_buffer_following_symlinks(appImage_type_2_file_path.c_str(), "echo.desktop", &buf, &bufsize);

// using EXPECT makes sure the free call is executed
EXPECT_TRUE(rv);
EXPECT_TRUE(buf != NULL);
EXPECT_TRUE(bufsize != 0);

static const char expected[] = ("[Desktop Entry]\n"
"Version=1.0\n"
"Type=Application\n"
"Name=Echo\n"
"Comment=Just echo.\n"
"Exec=echo %F\n"
"Icon=utilities-terminal\n");

EXPECT_EQ(bufsize, strlen(expected));
EXPECT_TRUE(buf != NULL && strncmp(expected, buf, bufsize) == 0);
free(buf);
}

TEST_F(LibAppImageTest, appimage_read_file_into_buffer_following_symlinks_type_1) {
char* buf = NULL;
unsigned long bufsize = 0;
bool rv = appimage_read_file_into_buffer_following_symlinks(appImage_type_1_file_path.c_str(), "AppImageExtract.desktop", &buf, &bufsize);

// using EXPECT makes sure the free call is executed
EXPECT_TRUE(rv);
EXPECT_TRUE(buf != NULL);
EXPECT_TRUE(bufsize != 0);

static const char expected[] = ("[Desktop Entry]\n"
"Name=AppImageExtract\n"
"Exec=appimageextract\n"
"Icon=AppImageExtract\n"
"Terminal=true\n"
"Type=Application\n"
"Categories=Development;\n"
"Comment=Extract AppImage contents, part of AppImageKit\n"
"StartupNotify=true\n");

EXPECT_EQ(bufsize, strlen(expected));
EXPECT_TRUE(buf != NULL && strncmp(expected, buf, bufsize) == 0);
free(buf);
}

bool test_appimage_is_registered_in_system(const std::string &pathToAppImage, bool integrateAppImage) {
if (integrateAppImage) {
EXPECT_EQ(appimage_register_in_system(pathToAppImage.c_str(), false), 0);
Expand Down

0 comments on commit 705935f

Please sign in to comment.