From 020d7c23b17956098379140f1f0047ae8e78df1b Mon Sep 17 00:00:00 2001 From: Zach C Date: Sun, 10 Aug 2008 17:24:32 -0700 Subject: Adds seeking, directory creation and improves file writing. Also various other cleanups. Implements creating directories as well as writing and deleting files in iFuse. Signed-off-by: Matthew Colyer --- src/AFC.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++----------- src/AFC.h | 13 ++++- src/ifuse.c | 131 +++++++++++++++++++++++++++++++++++++++++-- src/main.c | 24 +++++++- 4 files changed, 306 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/AFC.c b/src/AFC.c index 6fb5a9e..6a0df50 100644 --- a/src/AFC.c +++ b/src/AFC.c @@ -29,13 +29,15 @@ extern int debug; /* Locking, for thread-safety (well... kind of, hehe) */ void afc_lock(AFClient *client) { + if (debug) printf("In the midst of a lock...\n"); while (client->lock) { - sleep(200); + usleep(500); // they say it's obsolete, but whatever } client->lock = 1; } void afc_unlock(AFClient *client) { // just to be pretty + if (debug) printf("Unlock!\n"); client->lock = 0; } @@ -102,13 +104,13 @@ int dispatch_AFC_packet(AFClient *client, const char *data, int length) { if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) { if (debug) printf("dispatch_AFC_packet: Length did not resemble what it was supposed to based on the packet.\nlength minus offset: %i\nrest of packet: %i\n", length-offset, client->afc_packet->entire_length - client->afc_packet->this_length); free(buffer); - return 0; + return -1; } if (debug) printf("dispatch_AFC_packet: fucked-up packet method (probably a write)\n"); memcpy(buffer+sizeof(AFCPacket), data, offset); bytes = mux_send(client->connection, buffer, client->afc_packet->this_length); free(buffer); - if (bytes <= 0) { return 0; } + if (bytes <= 0) { return bytes; } if (debug) { printf("dispatch_AFC_packet: sent the first now go with the second\n"); printf("Length: %i\n", length-offset); @@ -118,8 +120,7 @@ int dispatch_AFC_packet(AFClient *client, const char *data, int length) { bytes = mux_send(client->connection, data+offset, length-offset); - if (bytes <= 0) { return 0; } - else { return bytes; } + return bytes; } else { if (debug) printf("dispatch_AFC_packet doin things the old way\n"); char *buffer = (char*)malloc(sizeof(char) * client->afc_packet->this_length); @@ -130,10 +131,9 @@ int dispatch_AFC_packet(AFClient *client, const char *data, int length) { if (debug) fwrite(buffer, 1, client->afc_packet->this_length, stdout); if (debug) printf("\n"); bytes = mux_send(client->connection, buffer, client->afc_packet->this_length); - if (bytes <= 0) return 0; - else return bytes; + return bytes; } - return 0; + return -1; } int receive_AFC_data(AFClient *client, char **dump_here) { @@ -148,7 +148,7 @@ int receive_AFC_data(AFClient *client, char **dump_here) { free(buffer); printf("Just didn't get enough.\n"); *dump_here = NULL; - return 0; + return -1; } r_packet = (AFCPacket*)malloc(sizeof(AFCPacket)); @@ -177,19 +177,19 @@ int receive_AFC_data(AFClient *client, char **dump_here) { if(param1 == 0) { if (debug) printf("... false alarm, but still\n"); *dump_here = NULL; - return 1; + return 0; } else { if (debug) printf("Errno %i\n", param1); } free(r_packet); *dump_here = NULL; - return 0; + return -1; } else { if (debug) printf("Operation code %x\nFull length %i and this length %i\n", r_packet->operation, r_packet->entire_length, r_packet->this_length); } recv_len = r_packet->entire_length - r_packet->this_length; free(r_packet); - if (!recv_len) + if (!recv_len && r_packet->operation == AFC_SUCCESS_RESPONSE) { *dump_here = NULL; return 0; @@ -240,10 +240,28 @@ char **afc_get_dir_list(AFClient *client, const char *dir) { char *blah = NULL, **list = NULL; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes = dispatch_AFC_packet(client, dir, strlen(dir)); - if (!bytes) { afc_unlock(client); return NULL; } + if (bytes <= 0) { afc_unlock(client); return NULL; } + + bytes = receive_AFC_data(client, &blah); + if (bytes < 0 && !blah) { afc_unlock(client); return NULL; } + + list = make_strings_list(blah, bytes); + free(blah); + afc_unlock(client); + return list; +} + +char **afc_get_devinfo(AFClient *client) { + afc_lock(client); + client->afc_packet->operation = AFC_GET_DEVINFO; + int bytes = 0; + char *blah = NULL, **list = NULL; + client->afc_packet->entire_length = client->afc_packet->this_length = 0; + bytes = dispatch_AFC_packet(client, NULL, 0); + if (bytes < 0) { afc_unlock(client); return NULL; } bytes = receive_AFC_data(client, &blah); - if (!bytes && !blah) { afc_unlock(client); return NULL; } + if (bytes < 0 && !blah) { afc_unlock(client); return NULL; } list = make_strings_list(blah, bytes); free(blah); @@ -251,6 +269,7 @@ char **afc_get_dir_list(AFClient *client, const char *dir) { return list; } + char **make_strings_list(char *tokens, int true_length) { if (!tokens || !true_length) return NULL; int nulls = 0, i = 0, j = 0; @@ -279,7 +298,7 @@ int afc_delete_file(AFClient *client, const char *path) { bytes = receive_AFC_data(client, &receive); free(receive); afc_unlock(client); - if (bytes <= 0) { return 0; } + if (bytes < 0) { return 0; } else return 1; } @@ -302,12 +321,28 @@ int afc_rename_file(AFClient *client, const char *from, const char *to) { bytes = receive_AFC_data(client, &receive); free(receive); afc_unlock(client); - if (bytes <= 0) return 0; + if (bytes < 0) return 0; else return 1; } +int afc_mkdir(AFClient *client, const char *dir) { + if (!client) return 0; + afc_lock(client); + int bytes = 0; + char *recvd = NULL; + client->afc_packet->operation = AFC_MAKE_DIR; + client->afc_packet->this_length = client->afc_packet->entire_length = 0; + bytes = dispatch_AFC_packet(client, dir, strlen(dir)); + if (bytes <= 0) { afc_unlock(client); return 0; } + bytes = receive_AFC_data(client, &recvd); + afc_unlock(client); + if (recvd) { free(recvd); recvd = NULL; } + if (bytes == 0) return 1; + else return 0; +} + AFCFile *afc_get_file_info(AFClient *client, const char *path) { client->afc_packet->operation = AFC_GET_INFO; client->afc_packet->entire_length = client->afc_packet->this_length = 0; @@ -350,7 +385,7 @@ AFCFile *afc_get_file_info(AFClient *client, const char *path) { } AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode) { - if (file_mode != AFC_FILE_READ && file_mode != AFC_FILE_WRITE) return NULL; + //if (file_mode != AFC_FILE_READ && file_mode != AFC_FILE_WRITE) return NULL; if (!client ||!client->connection || !client->afc_packet) return NULL; afc_lock(client); char *further_data = (char*)malloc(sizeof(char) * (8 + strlen(filename) + 1)); @@ -372,17 +407,19 @@ AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode) return NULL; } else { length_thing = receive_AFC_data(client, &further_data); - if (length_thing && further_data) { + if (length_thing > 0 && further_data) { afc_unlock(client); // don't want to hang on the next call... and besides, it'll re-lock, do its thing, and unlock again anyway. file_infos = afc_get_file_info(client, filename); memcpy(&file_infos->filehandle, further_data, 4); return file_infos; } else { if (debug) printf("didn't get further data or something\n"); + afc_unlock(client); return NULL; } } if (debug) printf("what the fuck\n"); + afc_unlock(client); return NULL; } @@ -408,7 +445,8 @@ int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) { // Looping here to get around the maximum amount of data that recieve_AFC_data can handle while (current_count < length){ - + if (debug) printf("afc_read_file: current count is %i but length is %i\n", current_count, length); + // Send the read command AFCFilePacket *packet = (AFCFilePacket*)malloc(sizeof(AFCFilePacket)); packet->unknown1 = packet->unknown2 = 0; @@ -421,6 +459,7 @@ int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) { // If we get a positive reponse to the command gather the data if (bytes > 0) { bytes = receive_AFC_data(client, &input); + if (debug) printf("afc_read_file: bytes returned: %i\n", bytes); if (bytes < 0) { if (input) free(input); afc_unlock(client); @@ -431,7 +470,7 @@ int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) { return current_count; } else { if (input) { - printf("afc_read_file: %d\n", bytes); + if (debug) printf("afc_read_file: %d\n", bytes); memcpy(data+current_count, input, (bytes > length) ? length : bytes); free(input); input = NULL; @@ -444,6 +483,7 @@ int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) { } } afc_unlock(client); + if (debug) printf("afc_read_file: returning current_count as %i\n", current_count); return current_count; } @@ -451,29 +491,53 @@ int afc_write_file(AFClient *client, AFCFile *file, const char *data, int length char *acknowledgement = NULL; if (!client ||!client->afc_packet || !client->connection || !file) return -1; afc_lock(client); - client->afc_packet->this_length = sizeof(AFCPacket) + 8; - client->afc_packet->entire_length = client->afc_packet->this_length + length; - client->afc_packet->operation = AFC_WRITE; if (debug) printf("afc_write_file: Write length: %i\n", length); - uint32 zero = 0, bytes = 0; - + const int MAXIMUM_WRITE_SIZE = 1 << 16; + uint32 zero = 0, bytes = 0, segments = (length / MAXIMUM_WRITE_SIZE), current_count = 0, i = 0; char *out_buffer = NULL; + + for (i = 0; i < segments; i++) { // Essentially, yeah, divide it into segments. + client->afc_packet->this_length = sizeof(AFCPacket) + 8; + //client->afc_packet->entire_length = client->afc_packet->this_length + length; + client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; + client->afc_packet->operation = AFC_WRITE; + out_buffer = (char*)malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); + memcpy(out_buffer, (char*)&file->filehandle, sizeof(uint32)); + memcpy(out_buffer+4, (char*)&zero, sizeof(uint32)); + memcpy(out_buffer+8, data+current_count, MAXIMUM_WRITE_SIZE); + + bytes = dispatch_AFC_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8); + if (bytes < 0) { afc_unlock(client); return bytes; } + free(out_buffer); out_buffer = NULL; // cleanup and hope it works + current_count += bytes; + bytes = receive_AFC_data(client, &acknowledgement); + if (bytes < 0) { afc_unlock(client); return current_count; } + } + + // By this point, we should be at the end. i.e. the last segment that didn't get sent in the for loop + // this length is fine because it's always sizeof(AFCPacket) + 8, but to be sure we do it again + if (current_count == length) { afc_unlock(client); return current_count; } + client->afc_packet->this_length = sizeof(AFCPacket) + 8; + client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); + // operation is already AFC_WRITE, but set again to be sure + client->afc_packet->operation = AFC_WRITE; out_buffer = (char*)malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); memcpy(out_buffer, (char*)&file->filehandle, sizeof(uint32)); memcpy(out_buffer+4, (char*)&zero, sizeof(uint32)); - memcpy(out_buffer+8, data, length); - - bytes = dispatch_AFC_packet(client, out_buffer, length + 8); - if (!bytes) return -1; + memcpy(out_buffer+8, data+current_count, (length - current_count)); + bytes = dispatch_AFC_packet(client, out_buffer, (length - current_count) + 8); + free(out_buffer); out_buffer = NULL; + current_count += bytes; + if (bytes <= 0) { afc_unlock(client); return current_count; } zero = bytes; bytes = receive_AFC_data(client, &acknowledgement); afc_unlock(client); - if (bytes <= 0) { + if (bytes < 0) { if (debug) printf("afc_write_file: uh oh?\n"); } - return zero; + return current_count; } void afc_close_file(AFClient *client, AFCFile *file) { @@ -490,9 +554,61 @@ void afc_close_file(AFClient *client, AFCFile *file) { free(buffer); client->afc_packet->entire_length = client->afc_packet->this_length = 0; - if (!bytes) return; + if (bytes <= 0) { afc_unlock(client); return; } bytes = receive_AFC_data(client, &buffer); afc_unlock(client); + if (buffer) { free(buffer); } return; } + +int afc_seek_file(AFClient *client, AFCFile *file, int seekpos) { + afc_lock(client); + + char *buffer = (char*)malloc(sizeof(char) * 24); + uint32 seekto = 0, bytes = 0, zero = 0; + if (seekpos < 0) seekpos = file->size - abs(seekpos); + seekto = seekpos; + + memcpy(buffer, &file->filehandle, sizeof(uint32)); // handle + memcpy(buffer+4, &zero, sizeof(uint32)); // pad + memcpy(buffer+8, &zero, sizeof(uint32)); // fromwhere + memcpy(buffer+12, &zero, sizeof(uint32)); // pad + memcpy(buffer+16, &seekto, sizeof(uint32)); // offset + memcpy(buffer+20, &zero, sizeof(uint32)); // pad + client->afc_packet->operation = AFC_FILE_SEEK; + client->afc_packet->this_length = client->afc_packet->entire_length = 0; + bytes = dispatch_AFC_packet(client, buffer, 23); + free(buffer); buffer = NULL; + if (bytes <= 0) { afc_unlock(client); return -1; } + + bytes = receive_AFC_data(client, &buffer); + if (buffer) free(buffer); + afc_unlock(client); + if (bytes >= 0) return 0; + else return -1; +} + +int afc_truncate_file(AFClient *client, AFCFile *file, uint32 newsize) { + afc_lock(client); + + char *buffer = (char*)malloc(sizeof(char) * 16); + uint32 bytes = 0, zero = 0; + + memcpy(buffer, &file->filehandle, sizeof(uint32)); // handle + memcpy(buffer+4, &zero, sizeof(uint32)); // pad + memcpy(buffer+8, &newsize, sizeof(uint32)); // newsize + memcpy(buffer+12, &zero, 3); // pad + client->afc_packet->operation = AFC_FILE_TRUNCATE; + client->afc_packet->this_length = client->afc_packet->entire_length = 0; + bytes = dispatch_AFC_packet(client, buffer, 15); + free(buffer); buffer = NULL; + if (bytes <= 0) { afc_unlock(client); return -1; } + + bytes = receive_AFC_data(client, &buffer); + if (buffer) free(buffer); + afc_unlock(client); + if (bytes >= 0) return 0; + else return -1; +} + diff --git a/src/AFC.h b/src/AFC.h index 32defbe..39ada91 100644 --- a/src/AFC.h +++ b/src/AFC.h @@ -56,8 +56,14 @@ typedef struct __AFCToken { enum { - AFC_FILE_READ = 0x00000002, - AFC_FILE_WRITE = 0x00000003 + AFC_FILE_READ = 0x00000002, // seems to be able to read and write files + AFC_FILE_WRITE = 0x00000003, // writes and creates a file, blanks it out, etc. + AFC_FILE_RW = 0x00000005, // seems to do the same as 2. Might even create the file. + AFC_FILE_OP4 = 0x00000004, // no idea -- appears to be "write" -- clears file beforehand like 3 + AFC_FILE_OP6 = 0x00000006, // no idea yet -- appears to be the same as 5. + AFC_FILE_OP1 = 0x00000001, // no idea juuust yet... probably read. + AFC_FILE_OP0 = 0x00000000, + AFC_FILE_OP10 = 0x0000000a }; enum { @@ -65,11 +71,14 @@ enum { AFC_GET_INFO = 0x0000000a, AFC_GET_DEVINFO = 0x0000000b, AFC_LIST_DIR = 0x00000003, + AFC_MAKE_DIR = 0x00000009, AFC_DELETE = 0x00000008, AFC_RENAME = 0x00000018, AFC_SUCCESS_RESPONSE = 0x00000002, AFC_FILE_OPEN = 0x0000000d, AFC_FILE_CLOSE = 0x00000014, + AFC_FILE_SEEK = 0x00000011, + AFC_FILE_TRUNCATE = 0x00000015, AFC_FILE_HANDLE = 0x0000000e, AFC_READ = 0x0000000f, AFC_WRITE = 0x00000010 diff --git a/src/ifuse.c b/src/ifuse.c index 3d921fd..9fc1ad8 100644 --- a/src/ifuse.c +++ b/src/ifuse.c @@ -41,7 +41,7 @@ GHashTable *file_handles; int fh_index = 0; -int debug = 1; +int debug = 0; static int ifuse_getattr(const char *path, struct stat *stbuf) { int res = 0; @@ -53,7 +53,8 @@ static int ifuse_getattr(const char *path, struct stat *stbuf) { if (!file){ res = -ENOENT; } else { - stbuf->st_mode = file->type | 0444; + //stbuf->st_mode = file->type | 0444; // testing write access too now + stbuf->st_mode = file->type | 0644; // but we don't want anything on the iPhone executable, like, ever stbuf->st_size = file->size; //stbuf->st_nlink = 2; } @@ -81,14 +82,34 @@ static int ifuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, return 0; } -static int ifuse_open(const char *path, struct fuse_file_info *fi) { +static int ifuse_create(const char *path, mode_t mode, struct fuse_file_info *fi) { + // exactly the same as open but using a different mode AFCFile *file; AFClient *afc = fuse_get_context()->private_data; + + file = afc_open_file(afc, path, AFC_FILE_WRITE); + fh_index++; + fi->fh = fh_index; + g_hash_table_insert(file_handles, &fh_index, file); + return 0; +} - if((fi->flags & 3) != O_RDONLY) - return -EACCES; +static int ifuse_open(const char *path, struct fuse_file_info *fi) { + AFCFile *file; + AFClient *afc = fuse_get_context()->private_data; + uint32 mode = 0; + /*if((fi->flags & 3) != O_RDONLY) + return -EACCES;*/ // trying to test write access here + + if ((fi->flags & 3) == O_RDWR || (fi->flags & 3) == O_WRONLY) { + mode = AFC_FILE_READ; + } else if ((fi->flags & 3) == O_RDONLY) { + mode = AFC_FILE_READ; + } else { + mode = AFC_FILE_READ; + } - file = afc_open_file(afc, path, AFC_FILE_READ); + file = afc_open_file(afc, path, mode); fh_index++; fi->fh = fh_index; @@ -111,10 +132,30 @@ static int ifuse_read(const char *path, char *buf, size_t size, off_t offset, return -ENOENT; } + bytes = afc_seek_file(afc, file, offset); bytes = afc_read_file(afc, file, buf, size); return bytes; } +static int ifuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + int bytes = 0; + AFCFile *file = NULL; + AFClient *afc = fuse_get_context()->private_data; + + if (size == 0) return 0; + + file = g_hash_table_lookup(file_handles, &(fi->fh)); + if (!file) return -ENOENT; + + bytes = afc_seek_file(afc, file, offset); + bytes = afc_write_file(afc, file, buf, size); + return bytes; +} + +static int ifuse_fsync(const char *path, int datasync, struct fuse_file_info *fi) { + return 0; +} + static int ifuse_release(const char *path, struct fuse_file_info *fi){ AFCFile *file; AFClient *afc = fuse_get_context()->private_data; @@ -181,11 +222,89 @@ void ifuse_cleanup(void *data) { } } +int ifuse_flush(const char *path, struct fuse_file_info *fi) { + return 0; +} + +int ifuse_statfs(const char *path, struct statvfs *stats) { + AFClient *afc = fuse_get_context()->private_data; + char **info_raw = afc_get_devinfo(afc); + uint32 totalspace = 0, freespace = 0, blocksize = 0, i = 0; + + if (!info_raw) return -ENOENT; + + for (i = 0; strcmp(info_raw[i], ""); i++) { + if (!strcmp(info_raw[i], "FSTotalBytes")) { + totalspace = atoi(info_raw[i+1]); + } else if (!strcmp(info_raw[i], "FSFreeBytes")) { + freespace = atoi(info_raw[i+1]); + } else if (!strcmp(info_raw[i], "FSBlockSize")) { + blocksize = atoi(info_raw[i+1]); + } + } + + // Now to fill the struct. + stats->f_bsize = stats->f_frsize = blocksize; + stats->f_blocks = totalspace / blocksize; // gets the blocks by dividing bytes by blocksize + stats->f_bfree = stats->f_bavail = freespace / blocksize; // all bytes are free to everyone, I guess. + stats->f_namemax = 255; // blah + stats->f_files = stats->f_ffree = 1000000000; // make up any old thing, I guess + return 0; +} + +int ifuse_truncate(const char *path, off_t size) { + int result = 0; + AFClient *afc = fuse_get_context()->private_data; + AFCFile *tfile = afc_open_file(afc, path, AFC_FILE_READ); + if (!tfile) return -1; + + result = afc_truncate_file(afc, tfile, size); + afc_close_file(afc, tfile); + return result; +} + +int ifuse_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { + int result = 0; + AFClient *afc = fuse_get_context()->private_data; + AFCFile *file = g_hash_table_lookup(file_handles, &fi->fh); + if (!file) return -ENOENT; + + return afc_truncate_file(afc, file, size); +} + +int ifuse_unlink(const char *path) { + AFClient *afc = fuse_get_context()->private_data; + if (afc_delete_file(afc, path)) return 0; + else return -1; +} + +int ifuse_rename(const char *from, const char *to) { + AFClient *afc = fuse_get_context()->private_data; + if (afc_rename_file(afc, from, to)) return 0; + else return -1; +} + +int ifuse_mkdir(const char *dir, mode_t ignored) { + AFClient *afc = fuse_get_context()->private_data; + if (afc_mkdir(afc, dir)) return 0; + else return -1; +} + static struct fuse_operations ifuse_oper = { .getattr = ifuse_getattr, + .statfs = ifuse_statfs, .readdir = ifuse_readdir, + .mkdir = ifuse_mkdir, + .rmdir = ifuse_unlink, // AFC uses the same op for both. + .create = ifuse_create, .open = ifuse_open, .read = ifuse_read, + .write = ifuse_write, + .truncate = ifuse_truncate, + .ftruncate = ifuse_ftruncate, + .unlink = ifuse_unlink, + .rename = ifuse_rename, + .fsync = ifuse_fsync, .release = ifuse_release, .init = ifuse_init, .destroy = ifuse_cleanup diff --git a/src/main.c b/src/main.c index fd2a0d4..91bb48c 100644 --- a/src/main.c +++ b/src/main.c @@ -80,6 +80,13 @@ int main(int argc, char *argv[]) { } free_dictionary(dirs); + dirs = afc_get_devinfo(afc); + if (dirs) { + for (i = 0; strcmp(dirs[i], ""); i+=2) { + printf("%s: %s\n", dirs[i], dirs[i+1]); + } + } + AFCFile *my_file = afc_open_file(afc, "/iTunesOnTheGoPlaylist.plist", AFC_FILE_READ); if (my_file) { printf("A file size: %i\n", my_file->size); @@ -108,12 +115,25 @@ int main(int argc, char *argv[]) { printf("Deleting a file...\n"); bytes = afc_delete_file(afc, "/delme"); if (bytes) printf("Success.\n"); - else printf("Failure.\n"); + else printf("Failure. (expected unless you have a /delme file on your phone)\n"); printf("Renaming a file...\n"); bytes = afc_rename_file(afc, "/renme", "/renme2"); if (bytes > 0) printf("Success.\n"); - else printf("Failure.\n"); + else printf("Failure. (expected unless you have a /renme file on your phone)\n"); + + printf("Seek & read\n"); + my_file = afc_open_file(afc, "/readme.libiphone.fx", AFC_FILE_READ); + bytes = afc_seek_file(afc, my_file, 5); + if (!bytes) printf("WARN: SEEK DID NOT WORK\n"); + char *threeletterword = (char*)malloc(sizeof(char) * 5); + bytes = afc_read_file(afc, my_file, threeletterword, 3); + threeletterword[3] = '\0'; + if (bytes > 0) printf("Result: %s\n", threeletterword); + else printf("Couldn't read!\n"); + free(threeletterword); + afc_close_file(afc, my_file); + } afc_disconnect(afc); } else { -- cgit v1.1-32-gdbae