Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory traversal support #830

Merged
merged 42 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9cb4f41
mem_acquire no longer allows returning NULL, initial prototype of fil…
JonathanHenson Aug 3, 2021
1dc5cd4
Reworked api. File system ops are now just done on strings free form.…
JonathanHenson Aug 12, 2021
b59cbcf
Merge branch 'main' into directory_support
JonathanHenson Aug 13, 2021
2b732b4
Cleaned up allocator api, added some documentation on the NULL return…
JonathanHenson Aug 13, 2021
121f474
Merge branch 'main' into directory_support
JonathanHenson Aug 13, 2021
3806724
actually be posix compliant.
JonathanHenson Aug 13, 2021
e35a908
fix memory leak from realpath()
JonathanHenson Aug 13, 2021
21dbc82
address PR comments. Added some comments, removed allocator from dire…
JonathanHenson Aug 17, 2021
24614b0
cleaned up wchar conversion apis, moved them to aws_string. Deprecate…
JonathanHenson Aug 17, 2021
e40d40f
be more const correct.
JonathanHenson Aug 17, 2021
b0a6206
Early windows work. Sending to CI.
JonathanHenson Aug 18, 2021
0eb96a9
windows comiple fixes.
JonathanHenson Aug 19, 2021
5aad3e5
fix the fopen code.
JonathanHenson Aug 19, 2021
869d85d
Not like that tho
JonathanHenson Aug 19, 2021
d1b88e7
More build fixes.
JonathanHenson Aug 19, 2021
ab2bebe
more build fixes.
JonathanHenson Aug 19, 2021
f1e6bca
github actions are my windows IDE.
JonathanHenson Aug 19, 2021
713c7d5
Okay tests pass now on windows. Still need to do a format run from a …
JonathanHenson Aug 26, 2021
101ffe6
Just formatting.
JonathanHenson Aug 26, 2021
72f48a8
Remove unneeded headers.
JonathanHenson Aug 26, 2021
7cf1b2e
Better error handling, deleted some old test tracing markers for debu…
JonathanHenson Aug 26, 2021
19f1b3a
Formatting
JonathanHenson Aug 26, 2021
9abb90a
Merge branch 'main' into directory_support
JonathanHenson Aug 26, 2021
23eb6c1
hopefully fix the cbmc proofs.
JonathanHenson Aug 26, 2021
b79a295
Let us pray.
JonathanHenson Aug 27, 2021
8ce6b55
posix_memalign return nonsense.
JonathanHenson Aug 27, 2021
9b7bd0b
God I'm sick of CBMC.
JonathanHenson Aug 27, 2021
53a0107
Try using assumptions instead?
JonathanHenson Aug 27, 2021
dc39a5b
Revert assumptions.
JonathanHenson Aug 27, 2021
d5983d6
Added oom panic macro to aid in formal verification of allocators.
JonathanHenson Aug 30, 2021
baba607
Lets try again.
JonathanHenson Aug 30, 2021
45d6ee8
figure it oot
JonathanHenson Aug 30, 2021
efdc2cd
Merge from main, conflicts abounded.
JonathanHenson Oct 11, 2021
7dd6922
Ran formatter from reasonably decent toolchain.
JonathanHenson Oct 11, 2021
c5165a1
Tests pass again. aws_wstring is now a thing.
JonathanHenson Oct 11, 2021
c0346a2
Ran formatter.
JonathanHenson Oct 11, 2021
6a82516
addressed remaining PR comments.
JonathanHenson Oct 11, 2021
0f39b46
Merge branch 'directory_support' of github.com:awslabs/aws-c-common i…
JonathanHenson Oct 11, 2021
d1ad6cc
Formatting.
JonathanHenson Oct 11, 2021
e6757bc
Added iterator api for file-system API.
JonathanHenson Oct 12, 2021
6f3a30a
Merge branch 'main' into directory_support
JonathanHenson Oct 12, 2021
711da9c
Merge branch 'main' into directory_support
JonathanHenson Oct 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions include/aws/common/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,58 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/common/common.h>
#include <aws/common/linked_list.h>
#include <aws/common/ref_count.h>
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved

#ifdef _WIN32
static const char AWS_PATH_DELIM = '\\';
#else
static const char AWS_PATH_DELIM = '/';
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
#endif

enum aws_file_type {
AWS_FILE_TYPE_NONE,
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
AWS_FILE_TYPE_FILE,
AWS_FILE_TYPE_SYM_LINK,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we get rid of this enum and just add aws_directory_entry_is_file(), _is_directory(), _is_symlink(), etc. Since an entry can be symlink AND file, or symlink AND directory.

That's the approach taken by std::filesystem::directory_entry

AWS_FILE_TYPE_DIRECTORY,
};

struct aws_directory_entry {
struct aws_allocator *allocator;
struct aws_string *path;
struct aws_string *relative_path;
enum aws_file_type file_type;
int64_t file_size;
struct aws_linked_list children;
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
struct aws_linked_list_node node;
struct aws_directory_entry *parent;
struct aws_ref_count ref_count;
void *impl;
};
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved

/**
* Platform implementation of opening a file and loading its immediate children (not recursive).
* return AWS_OP_SUCCESS if successful.
*/
int aws_directory_entry_open_internal(struct aws_directory_entry *entry);

/**
* Destroy any resources allocated in aws_directory_entry_open_internal.
*/
void aws_directory_entry_destroy_internal(struct aws_directory_entry *entry);
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved

/**
* General function for allocating and setting up a directory entry. Use for allocating and setting up the
* platform agnostic portions of aws_directory_entry.
*
* returns an instance of aws_directory_entry on success and NULL on failure.
*/
struct aws_directory_entry *aws_directory_entry_open_base(
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
struct aws_allocator *allocator,
struct aws_byte_cursor path,
struct aws_byte_cursor relative_path);

AWS_EXTERN_C_BEGIN

Expand All @@ -17,6 +68,54 @@ AWS_EXTERN_C_BEGIN
AWS_COMMON_API
FILE *aws_fopen(const char *file_path, const char *mode);

/**
* Opens a directory at path. It's ref-count will be initialized to 1 on successful return.
* You must call aws_directory_entry_release() when finished or the resource will leak.
*
* This function allocates resources for children in the directory, but only with a depth of one. To get to the
* next level, you call aws_directory_entry_descend() which will repeat this process.
*/
AWS_COMMON_API struct aws_directory_entry *aws_directory_entry_open(
struct aws_allocator *allocator,
struct aws_byte_cursor path,
struct aws_byte_cursor relative_path);
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved

/**
* Holds a reference to the entry. Call this before seating an instance of aws_directory_entry().
*/
AWS_COMMON_API void aws_directory_entry_acquire(struct aws_directory_entry *entry);

/**
* Releases reference to entry. When the last reference is dropped, the resource will be freed.
*/
AWS_COMMON_API void aws_directory_entry_release(struct aws_directory_entry *entry);

/**
* Get's the next directory entry at the current directory depth level as entry. Returns NULL when there are no more
* sibling entries.
*/
AWS_COMMON_API struct aws_directory_entry *aws_directory_entry_get_next_sibling(struct aws_directory_entry *entry);

/**
* Get's the previous directory entry at the current directory depth level as entry. Returns NULL when there are no more
* sibling entries.
*/
AWS_COMMON_API struct aws_directory_entry *aws_directory_entry_get_prev_sibling(struct aws_directory_entry *entry);

/**
* Descends into the next depth level for entry. Returns the first child in entry. Returns NULL if entry is not a
* directory or it's empty. The return value's reference count is owned by entry. You do not need to acquire unless
* you'll be seating the return value beyond the scope of the root entry.
*/
AWS_COMMON_API struct aws_directory_entry *aws_directory_entry_descend(struct aws_directory_entry *entry);

/**
* Returns the parent for the current entry unless the entry is the initial root directory from
* aws_directory_entry_open(). In that case it returns NULL. Also note, you do not need to acquire or release a
* reference to the returned value unless you plan on seating it beyond the scope of the root entry.
*/
AWS_COMMON_API struct aws_directory_entry *aws_directory_entry_parent(struct aws_directory_entry *entry);
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved

AWS_EXTERN_C_END

#endif /* AWS_COMMON_FILE_H */
41 changes: 21 additions & 20 deletions source/allocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) {

void *mem = allocator->mem_acquire(allocator, size);
if (!mem) {
aws_raise_error(AWS_ERROR_OOM);
AWS_FATAL_ASSERT(
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
mem && "memory allocation failed. If you don't want to exit when this happens, handle OOM in your "
"allocator implementation");
}
return mem;
}
Expand All @@ -135,23 +137,26 @@ void *aws_mem_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
*/
size_t required_bytes;
if (aws_mul_size_checked(num, size, &required_bytes)) {
return NULL;
AWS_FATAL_ASSERT("illegal size for calloc()");
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
}

/* If there is a defined calloc, use it */
if (allocator->mem_calloc) {
void *mem = allocator->mem_calloc(allocator, num, size);
if (!mem) {
aws_raise_error(AWS_ERROR_OOM);
AWS_FATAL_ASSERT(
mem && "memory allocation failed. If you don't want to exit when this happens, handle OOM in your "
"allocator implementation");
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
}
return mem;
}

/* Otherwise, emulate calloc */
void *mem = allocator->mem_acquire(allocator, required_bytes);
if (!mem) {
aws_raise_error(AWS_ERROR_OOM);
return NULL;
AWS_FATAL_ASSERT(
mem && "memory allocation failed. If you don't want to exit when this happens, handle OOM in your "
"allocator implementation");
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
}
memset(mem, 0, required_bytes);
AWS_POSTCONDITION(mem != NULL);
Expand Down Expand Up @@ -186,8 +191,9 @@ void *aws_mem_acquire_many(struct aws_allocator *allocator, size_t count, ...) {

allocation = aws_mem_acquire(allocator, total_size);
if (!allocation) {
aws_raise_error(AWS_ERROR_OOM);
goto cleanup;
AWS_FATAL_ASSERT(
allocation && "memory allocation failed. If you don't want to exit when this happens, handle OOM in "
"your allocator implementation");
}

uint8_t *current_ptr = allocation;
Expand All @@ -204,7 +210,6 @@ void *aws_mem_acquire_many(struct aws_allocator *allocator, size_t count, ...) {
}
}

cleanup:
va_end(args_allocs);
return allocation;
}
Expand Down Expand Up @@ -235,7 +240,9 @@ int aws_mem_realloc(struct aws_allocator *allocator, void **ptr, size_t oldsize,
if (allocator->mem_realloc) {
void *newptr = allocator->mem_realloc(allocator, *ptr, oldsize, newsize);
if (!newptr) {
return aws_raise_error(AWS_ERROR_OOM);
AWS_FATAL_ASSERT(
newptr && "memory allocation failed. If you don't want to exit when this happens, handle OOM in your "
"allocator implementation");
}
*ptr = newptr;
return AWS_OP_SUCCESS;
Expand All @@ -248,7 +255,9 @@ int aws_mem_realloc(struct aws_allocator *allocator, void **ptr, size_t oldsize,

void *newptr = allocator->mem_acquire(allocator, newsize);
if (!newptr) {
return aws_raise_error(AWS_ERROR_OOM);
AWS_FATAL_ASSERT(
newptr && "memory allocation failed. If you don't want to exit when this happens, handle OOM in your "
"allocator implementation");
}

memcpy(newptr, *ptr, oldsize);
Expand All @@ -274,10 +283,6 @@ static void *s_cf_allocator_allocate(CFIndex alloc_size, CFOptionFlags hint, voi

void *mem = aws_mem_acquire(allocator, (size_t)alloc_size + sizeof(size_t));

if (!mem) {
return NULL;
}

size_t allocation_size = (size_t)alloc_size + sizeof(size_t);
memcpy(mem, &allocation_size, sizeof(size_t));
return (void *)((uint8_t *)mem + sizeof(size_t));
Expand All @@ -301,9 +306,7 @@ static void *s_cf_allocator_reallocate(void *ptr, CFIndex new_size, CFOptionFlag
size_t original_size = 0;
memcpy(&original_size, original_allocation, sizeof(size_t));

if (aws_mem_realloc(allocator, &original_allocation, original_size, (size_t)new_size)) {
return NULL;
}
aws_mem_realloc(allocator, &original_allocation, original_size, (size_t)new_size);

size_t new_allocation_size = (size_t)new_size;
memcpy(original_allocation, &new_allocation_size, sizeof(size_t));
Expand Down Expand Up @@ -347,9 +350,7 @@ CFAllocatorRef aws_wrapped_cf_allocator_new(struct aws_allocator *allocator) {

cf_allocator = CFAllocatorCreate(NULL, &context);

if (!cf_allocator) {
aws_raise_error(AWS_ERROR_OOM);
}
AWS_FATAL_ASSERT(cf_allocator && "creation of cf allocator failed!");
feliperodri marked this conversation as resolved.
Show resolved Hide resolved

return cf_allocator;
}
Expand Down
117 changes: 117 additions & 0 deletions source/file.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/common/file.h>
#include <aws/common/string.h>

static void s_ref_dropped_callback(void *dir_ent) {
struct aws_directory_entry *entry = dir_ent;
aws_directory_entry_destroy_internal(entry);

while (!aws_linked_list_empty(&entry->children)) {
struct aws_linked_list_node *child = aws_linked_list_pop_front(&entry->children);
struct aws_directory_entry *child_entry = AWS_CONTAINER_OF(child, struct aws_directory_entry, node);
aws_directory_entry_release(child_entry);
}

aws_string_destroy(entry->path);
aws_string_destroy(entry->relative_path);
aws_mem_release(entry->allocator, entry);
}

struct aws_directory_entry *aws_directory_entry_open_base(
struct aws_allocator *allocator,
struct aws_byte_cursor path,
struct aws_byte_cursor relative_path) {
struct aws_directory_entry *entry = aws_mem_calloc(allocator, 1, sizeof(struct aws_directory_entry));

struct aws_byte_cursor trimmed_path = aws_byte_cursor_trim_pred(&path, aws_isspace);
struct aws_byte_cursor trimmed_relative_path = aws_byte_cursor_trim_pred(&relative_path, aws_isspace);

if (trimmed_path.len && trimmed_path.ptr[trimmed_path.len - 1] == AWS_PATH_DELIM) {
trimmed_path.len -= 1;
}

entry->path = aws_string_new_from_cursor(allocator, &trimmed_path);

if (trimmed_relative_path.len && trimmed_relative_path.ptr[trimmed_relative_path.len - 1] == AWS_PATH_DELIM) {
trimmed_relative_path.len -= 1;
}

entry->relative_path = aws_string_new_from_cursor(allocator, &trimmed_relative_path);

entry->allocator = allocator;
aws_ref_count_init(&entry->ref_count, entry, s_ref_dropped_callback);
aws_linked_list_init(&entry->children);

return entry;
}

struct aws_directory_entry *aws_directory_entry_open(
struct aws_allocator *allocator,
struct aws_byte_cursor path,
struct aws_byte_cursor relative_path) {
struct aws_directory_entry *entry = aws_directory_entry_open_base(allocator, path, relative_path);

if (aws_directory_entry_open_internal(entry)) {
aws_string_destroy(entry->path);
aws_string_destroy(entry->relative_path);
aws_mem_release(entry->allocator, entry);
return NULL;
JonathanHenson marked this conversation as resolved.
Show resolved Hide resolved
}

return entry;
}

void aws_directory_entry_acquire(struct aws_directory_entry *entry) {
aws_ref_count_acquire(&entry->ref_count);
}

void aws_directory_entry_release(struct aws_directory_entry *entry) {
aws_ref_count_release(&entry->ref_count);
}

struct aws_directory_entry *aws_directory_entry_get_next_sibling(struct aws_directory_entry *entry) {
if (aws_linked_list_node_next_is_valid(&entry->node)) {
struct aws_linked_list_node *sibling = aws_linked_list_next(&entry->node);
return AWS_CONTAINER_OF(sibling, struct aws_directory_entry, node);
}

return NULL;
}

struct aws_directory_entry *aws_directory_entry_get_prev_sibling(struct aws_directory_entry *entry) {
if (aws_linked_list_node_prev_is_valid(&entry->node)) {
struct aws_linked_list_node *sibling = aws_linked_list_prev(&entry->node);
return AWS_CONTAINER_OF(sibling, struct aws_directory_entry, node);
}

return NULL;
}

struct aws_directory_entry *aws_directory_entry_parent(struct aws_directory_entry *entry) {
return entry->parent;
}

struct aws_directory_entry *aws_directory_entry_descend(struct aws_directory_entry *entry) {
if (entry->file_type != AWS_FILE_TYPE_DIRECTORY && entry->file_type != AWS_FILE_TYPE_SYM_LINK) {
aws_raise_error(AWS_ERROR_FILE_INVALID_PATH);
return NULL;
}

if (aws_linked_list_empty(&entry->children)) {
if (aws_directory_entry_open_internal(entry)) {
return NULL;
}
}

if (!aws_linked_list_empty(&entry->children)) {
struct aws_linked_list_node *first_child = aws_linked_list_front(&entry->children);
return AWS_CONTAINER_OF(first_child, struct aws_directory_entry, node);
}

aws_raise_error(AWS_ERROR_FILE_INVALID_PATH);
return NULL;
}
Loading