char * zfile_digest (zfile_t *self) { if (!self->digest) { if (zfile_input (self) == -1) return NULL; // Problem reading file // Now calculate hash for file data, chunk by chunk size_t blocksz = 65535; off_t offset = 0; self->digest = zdigest_new (); zchunk_t *chunk = zfile_read (self, blocksz, offset); while (zchunk_size (chunk)) { zdigest_update (self->digest, zchunk_data (chunk), zchunk_size (chunk)); zchunk_destroy (&chunk); offset += blocksz; chunk = zfile_read (self, blocksz, offset); } zchunk_destroy (&chunk); zfile_close (self); } return zdigest_string (self->digest); }
const char * zfile_digest (zfile_t *self) { assert (self); if (!self->digest) { if (zfile_input (self) == -1) return NULL; // Problem reading file // Now calculate hash for file data, chunk by chunk size_t blocksz = 65535; off_t offset = 0; self->digest = zdigest_new (); if (!self->digest) return NULL; zchunk_t *chunk = zfile_read (self, blocksz, offset); while (zchunk_size (chunk)) { zdigest_update (self->digest, zchunk_data (chunk), zchunk_size (chunk)); zchunk_destroy (&chunk); // off_t is defined as long (32 bit on Windows, 64 bit otherwise) // This guards against overflow in both contexts. if (blocksz > LONG_MAX - offset) return NULL; offset += (off_t) blocksz; chunk = zfile_read (self, blocksz, offset); } zchunk_destroy (&chunk); fclose (self->handle); self->handle = 0; } return zdigest_string (self->digest); }
static void curl_destructor (CURL **curlp) { CURL *curl = *curlp; http_request *request; curl_easy_getinfo (curl, CURLINFO_PRIVATE, &request); zchunk_destroy (&request->response); zchunk_destroy (&request->body); curl_slist_free_all (request->headers); curl_easy_cleanup (curl); free (request); *curlp = NULL; }
int zfile_test (bool verbose) { printf (" * zfile: "); // @selftest zfile_t *file = zfile_new (".", "bilbo"); assert (streq (zfile_filename (file, "."), "bilbo")); assert (zfile_is_readable (file) == false); zfile_destroy (&file); // Create a test file in some random subdirectory file = zfile_new ("./this/is/a/test", "bilbo"); int rc = zfile_output (file); assert (rc == 0); zchunk_t *chunk = zchunk_new (NULL, 100); zchunk_fill (chunk, 0, 100); // Write 100 bytes at position 1,000,000 in the file rc = zfile_write (file, chunk, 1000000); assert (rc == 0); zfile_close (file); assert (zfile_is_readable (file)); assert (zfile_cursize (file) == 1000100); assert (!zfile_is_stable (file)); zchunk_destroy (&chunk); zclock_sleep (1001); assert (zfile_is_stable (file)); // Check we can read from file rc = zfile_input (file); assert (rc == 0); chunk = zfile_read (file, 1000100, 0); assert (chunk); assert (zchunk_size (chunk) == 1000100); zchunk_destroy (&chunk); // Remove file and directory zdir_t *dir = zdir_new ("./this", NULL); assert (zdir_cursize (dir) == 1000100); zdir_remove (dir, true); assert (zdir_cursize (dir) == 0); zdir_destroy (&dir); // Check we can no longer read from file assert (!zfile_is_readable (file)); rc = zfile_input (file); assert (rc == -1); zfile_destroy (&file); // @end printf ("OK\n"); return 0; }
void zhttp_client_test (bool verbose) { #if defined(HAVE_LIBCURL) && defined(ZMQ_STREAM) printf (" * zhttp_client: "); zsock_t *server = zsock_new_stream (NULL); int port = zsock_bind (server, "tcp://127.0.0.1:*"); char url[255]; sprintf (url, "http://127.0.0.1:%d", port); // @selftest // Simple create/destroy test zhttp_client_t *self = zhttp_client_new (verbose); assert (self); // Send the get request zlistx_t *headers = zlistx_new (); zlistx_add_end (headers, "Host: zeromq.org"); zhttp_client_get (self, url, headers, NULL); zlistx_destroy (&headers); // Receive request on the server zchunk_t *routing_id; char *request; int rc = zsock_recv (server, "cs", &routing_id, &request); assert (rc == 0); // Send the response char* response = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; zsock_send (server, "cs", routing_id, response); // Receive the response on the http client int code; zchunk_t *data; zhttp_client_recv (self, &code, &data, NULL); assert (zchunk_streq (data, "Hello")); // Sending another request, without being answer // Checking the client ability to stop while request are inprogres zhttp_client_get (self, url, NULL, NULL); zchunk_destroy (&data); zchunk_destroy (&routing_id); zstr_free (&request); zhttp_client_destroy (&self); zsock_destroy (&server); // @end printf ("OK\n"); #endif }
hydra_post_t * hydra_post_load (const char *filename) { assert (filename); zconfig_t *root = zconfig_loadf ("posts/%s", filename); if (!root) return NULL; // No such file hydra_post_t *self = NULL; char *ident = zconfig_resolve (root, "/post/ident", ""); char *subject = zconfig_resolve (root, "/post/subject", NULL); char *timestamp = zconfig_resolve (root, "/post/timestamp", NULL); char *parent_id = zconfig_resolve (root, "/post/parent-id", ""); char *mime_type = zconfig_resolve (root, "/post/mime-type", NULL); char *digest = zconfig_resolve (root, "/post/digest", NULL); char *location = zconfig_resolve (root, "/post/location", NULL); if (subject && timestamp && mime_type && digest && location && (strlen (ident) == ID_SIZE) && (strlen (parent_id) == 0 || strlen (parent_id) == ID_SIZE) && (strlen (timestamp) == 20) && (strlen (digest) == ID_SIZE)) { self = hydra_post_new (subject); strcpy (self->ident, ident); strcpy (self->timestamp, timestamp); strcpy (self->parent_id, parent_id); self->mime_type = strdup (mime_type); self->location = strdup (location); strcpy (self->digest, digest); self->content_size = atoll (zconfig_resolve (root, "/post/content-size", "0")); zchunk_destroy (&self->content); } zconfig_destroy (&root); return self; }
int hydra_post_save (hydra_post_t *self, const char *filename) { assert (self); assert (filename); // Creeate subdirectories if necessary zsys_dir_create ("posts"); zsys_dir_create ("posts/blobs"); // If post content hasn't yet been serialised, write it to disk in the // blobs directory and set the location property to point to it. if (self->content) { assert (!self->location); self->location = zsys_sprintf ("posts/blobs/%s", self->digest); FILE *output = fopen (self->location, "wb"); zchunk_write (self->content, output); zchunk_destroy (&self->content); fclose (output); } zconfig_t *root = zconfig_new ("root", NULL); zconfig_put (root, "/post/ident", hydra_post_ident (self)); zconfig_put (root, "/post/subject", self->subject); zconfig_put (root, "/post/timestamp", self->timestamp); zconfig_put (root, "/post/parent-id", self->parent_id); zconfig_put (root, "/post/mime-type", self->mime_type); zconfig_put (root, "/post/digest", self->digest); zconfig_put (root, "/post/location", self->location); zconfig_putf (root, "/post/content-size", "%ld", self->content_size); zconfig_savef (root, "posts/%s", filename); zconfig_destroy (&root); return 0; }
static void s_check_directory (s_agent_t *self) { // Get latest snapshot and build a patches list for any changes // All patches are built using a virtual path starting at "/" zdir_t *dir = zdir_new (self->path, NULL); zlist_t *patches = zdir_diff (self->dir, dir, "/"); // Drop old directory and replace with latest version zdir_destroy (&self->dir); self->dir = dir; while (zlist_size (patches)) { zdir_patch_t *patch = (zdir_patch_t *) zlist_pop (patches); if (zdir_patch_op (patch) == patch_create) { // Shout new files to DROPS group // Stupidest possible approach: send whole file as one frame // Truncate file at arbitrary limit of 10MB zfile_t *file = zdir_patch_file (patch); if (zfile_input (file) == 0) { zchunk_t *chunk = zfile_read (file, 10 * 1024 * 1024, 0); assert (chunk); zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "CREATE"); zmsg_addstr (msg, zdir_patch_vpath (patch)); zmsg_add (msg, zframe_new (zchunk_data (chunk), zchunk_size (chunk))); zchunk_destroy (&chunk); zyre_shout (self->zyre, "DROPS", &msg); } } zdir_patch_destroy (&patch); } zlist_destroy (&patches); }
// -------------------------------------------------------------------------- // Self test of this class int zchunk_test (bool verbose) { printf (" * zchunk: "); // @selftest zchunk_t *chunk = zchunk_new ("1234567890", 10); assert (chunk); assert (zchunk_size (chunk) == 10); assert (memcmp (zchunk_data (chunk), "1234567890", 10) == 0); zchunk_destroy (&chunk); chunk = zchunk_new (NULL, 10); zchunk_append (chunk, "12345678", 8); zchunk_append (chunk, "90ABCDEF", 8); zchunk_append (chunk, "GHIJKLMN", 8); assert (memcmp (zchunk_data (chunk), "1234567890", 10) == 0); assert (zchunk_size (chunk) == 10); zchunk_t *copy = zchunk_dup (chunk); assert (memcmp (zchunk_data (copy), "1234567890", 10) == 0); assert (zchunk_size (copy) == 10); zchunk_destroy (©); zchunk_destroy (&chunk); copy = zchunk_new ("1234567890abcdefghij", 20); chunk = zchunk_new (NULL, 8); zchunk_consume (chunk, copy); assert (!zchunk_exhausted (copy)); assert (memcmp (zchunk_data (chunk), "12345678", 8) == 0); zchunk_set (chunk, NULL, 0); zchunk_consume (chunk, copy); assert (!zchunk_exhausted (copy)); assert (memcmp (zchunk_data (chunk), "90abcdef", 8) == 0); zchunk_set (chunk, NULL, 0); zchunk_consume (chunk, copy); assert (zchunk_exhausted (copy)); assert (zchunk_size (chunk) == 4); assert (memcmp (zchunk_data (chunk), "ghij", 4) == 0); zchunk_destroy (©); zchunk_destroy (&chunk); // @end printf ("OK\n"); return 0; }
zconfig_t * zconfig_str_load (const char *string) { zchunk_t *chunk = zchunk_new (string, strlen (string)); zconfig_t *config = zconfig_chunk_load (chunk); zchunk_destroy (&chunk); return config; }
char * zconfig_str_save (zconfig_t *self) { zchunk_t *chunk = zconfig_chunk_save (self); char *string = strdup ((char *) zchunk_data (chunk)); zchunk_destroy (&chunk); return string; }
void zproto_example_set_public_key (zproto_example_t *self, zchunk_t **chunk_p) { assert (self); assert (chunk_p); zchunk_destroy (&self->public_key); self->public_key = *chunk_p; *chunk_p = NULL; }
void fmq_msg_set_chunk (fmq_msg_t *self, zchunk_t **chunk_p) { assert (self); assert (chunk_p); zchunk_destroy (&self->chunk); self->chunk = *chunk_p; *chunk_p = NULL; }
static void server_process_cluster_command ( server_t *self, const char *peer_id, const char *peer_name, zmsg_t *msg, bool unicast) { char *request = zmsg_popstr (msg); char *pipename = zmsg_popstr (msg); zsys_info ("peer=%s command=%s pipe=%s unicast=%d", peer_name, request, pipename? pipename: "-", unicast); // Lookup or create pipe // TODO: remote pipes need cleaning up with some timeout pipe_t *pipe = NULL; if (pipename) { pipe = (pipe_t *) zhash_lookup (self->pipes, pipename); if (!pipe) pipe = pipe_new (self, pipename); } if (pipe && streq (request, "HAVE WRITER")) pipe_attach_remote_writer (pipe, peer_id, unicast); else if (pipe && streq (request, "HAVE READER")) pipe_attach_remote_reader (pipe, peer_id, unicast); else if (pipe && streq (request, "DATA")) { // TODO encode these commands as proper protocol zframe_t *frame = zmsg_pop (msg); zchunk_t *chunk = zchunk_new (zframe_data (frame), zframe_size (frame)); if (pipe->writer == REMOTE_NODE && pipe->reader) { zsys_info ("send %d bytes to pipe", (int) zchunk_size (chunk)); pipe_send_data (pipe, &chunk); } else zsys_info ("discard %d bytes, unroutable", (int) zchunk_size (chunk)); zframe_destroy (&frame); zchunk_destroy (&chunk); } else if (pipe && streq (request, "DROP READER")) pipe_drop_remote_reader (&pipe, peer_id); else if (pipe && streq (request, "DROP WRITER")) pipe_drop_remote_writer (&pipe, peer_id); else if (streq (request, "DUMP")) zyre_dump (self->zyre); else zsys_warning ("bad request %s from %s", request, peer_name); zstr_free (&pipename); zstr_free (&request); }
static void client_terminate (client_t *self) { zchunk_t *chunk = (zchunk_t *) zlist_first (self->queue); while (chunk) { zchunk_destroy (&chunk); chunk = (zchunk_t *) zlist_next (self->queue); } zlist_destroy (&self->queue); }
void hydra_post_set_data (hydra_post_t *self, const void *data, size_t size) { assert (self); zstr_free (&self->location); zchunk_destroy (&self->content); self->content = zchunk_new (data, size); strcpy (self->digest, zchunk_digest (self->content)); self->content_size = zchunk_size (self->content); }
void hydra_post_test (bool verbose) { printf (" * hydra_post: "); if (verbose) printf ("\n"); // @selftest // Simple create/destroy test zsys_dir_create (".hydra_test"); zsys_dir_change (".hydra_test"); hydra_post_t *post = hydra_post_new ("Test post"); assert (post); hydra_post_set_content (post, "Hello, World"); assert (streq (hydra_post_mime_type (post), "text/plain")); char *content = hydra_post_content (post); assert (content); assert (streq (content, "Hello, World")); zstr_free (&content); int rc = hydra_post_save (post, "testpost"); assert (rc == 0); hydra_post_destroy (&post); post = hydra_post_load ("testpost"); assert (post); assert (hydra_post_content_size (post) == 12); if (verbose) hydra_post_print (post); content = hydra_post_content (post); assert (content); assert (streq (content, "Hello, World")); zstr_free (&content); zchunk_t *chunk = hydra_post_fetch ( post, hydra_post_content_size (post), 0); assert (chunk); assert (zchunk_size (chunk) == 12); zchunk_destroy (&chunk); hydra_post_t *copy = hydra_post_dup (post); assert (streq (hydra_post_ident (copy), hydra_post_ident (post))); hydra_post_destroy (&post); hydra_post_destroy (©); // Delete the test directory zsys_dir_change (".."); zdir_t *dir = zdir_new (".hydra_test", NULL); assert (dir); zdir_remove (dir, true); zdir_destroy (&dir); // @end printf ("OK\n"); }
static void parser_state_destroy(parser_state_t **state_p) { parser_state_t *state = *state_p; // must not destroy the pipe, as it's owned by the actor zsock_destroy(&state->pull_socket); zsock_destroy(&state->push_socket); zchunk_destroy(&state->decompression_buffer); free(state); *state_p = NULL; }
static void test_handler (void *arg, int response_code, zchunk_t *data) { (void) response_code; bool *event = (bool *) arg; *event = true; if (response_code == 200) assert (zchunk_streq (data, "Hello")); zchunk_destroy (&data); }
void hydra_post_destroy (hydra_post_t **self_p) { assert (self_p); if (*self_p) { hydra_post_t *self = *self_p; zstr_free (&self->subject); zstr_free (&self->mime_type); zstr_free (&self->location); zchunk_destroy (&self->content); free (self); *self_p = NULL; } }
char * hydra_post_content (hydra_post_t *self) { assert (self); char *content = NULL; if (self->mime_type && streq (self->mime_type, "text/plain")) { if (self->content) return zchunk_strdup (self->content); zchunk_t *chunk = hydra_post_fetch (self, 0, 0); // TODO: limit max size if (chunk) { content = zchunk_strdup (chunk); } zchunk_destroy (&chunk); } return content; }
static zchunk_t * recv_http_request(void* server) { zchunk_t *routing_id; char *request; int rc = zsock_recv (server, "cs", &routing_id, &request); assert (rc == 0); while (strlen (request) == 0) { zchunk_destroy (&routing_id); zstr_free (&request); zsock_recv (server, "cs", &routing_id, &request); assert (rc == 0); } zstr_free (&request); return routing_id; }
int hydra_post_set_file (hydra_post_t *self, const char *location) { assert (self); free (self->location); int rc = 0; self->location = strdup (location); zchunk_destroy (&self->content); zfile_t *file = zfile_new (NULL, self->location); if (file && zfile_is_readable (file)) { self->content_size = zfile_cursize (file); strcpy (self->digest, zfile_digest (file)); } else rc = -1; zfile_destroy (&file); return rc; }
void fmq_msg_destroy (fmq_msg_t **self_p) { assert (self_p); if (*self_p) { fmq_msg_t *self = *self_p; // Free class properties zframe_destroy (&self->routing_id); free (self->path); zhash_destroy (&self->options); zhash_destroy (&self->cache); free (self->filename); zhash_destroy (&self->headers); zchunk_destroy (&self->chunk); // Free object itself free (self); *self_p = NULL; } }
static void collect_data_to_send (client_t *self) { zsys_info ("read %d bytes", (int) zpipes_msg_size (self->request)); // Do we have enough data to satisfy the read request? size_t required = zpipes_msg_size (self->request); // If pipe was closed, we'll do a short read with as much // data as we have pending if (required > self->pending && self->pipe == NULL) required = self->pending; if (self->pipe == NULL && self->pending == 0) engine_set_exception (self, pipe_shut_event); else if (self->pending >= required) { // Create a bucket chunk with the required max size zchunk_t *bucket = zchunk_new (NULL, required); // Now fill the bucket with chunks from our queue while (zchunk_size (bucket) < required) { // Get next chunk and consume as much of it as possible zchunk_t *chunk = (zchunk_t *) zlist_pop (self->queue); assert (chunk); zchunk_consume (bucket, chunk); // If chunk is exhausted, destroy it if (zchunk_exhausted (chunk)) zchunk_destroy (&chunk); else { // Push chunk back for next time zlist_push (self->queue, chunk); assert (zchunk_size (bucket) == required); } } zpipes_msg_set_chunk (self->reply, &bucket); self->pending -= required; } else engine_set_exception (self, not_enough_data_event); }
static void pipe_send_data (pipe_t *self, zchunk_t **chunk_p) { assert (self); assert (self->reader); zchunk_t *chunk = *chunk_p; assert (chunk); if (self->reader == REMOTE_NODE) { // Send chunk to remote node reader zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "DATA"); zmsg_addstr (msg, self->name); zmsg_addmem (msg, zchunk_data (chunk), zchunk_size (chunk)); zyre_whisper (self->server->zyre, self->remote, &msg); zchunk_destroy (chunk_p); } else { client_store_chunk (self->reader, chunk_p); engine_send_event (self->reader, have_data_event); } }
zconfig_t * zconfig_load (const char *filename) { // Load entire file into memory as a chunk, then process it zconfig_t *self = NULL; zfile_t *file = zfile_new (NULL, filename); if (!file) return NULL; if (zfile_input (file) == 0) { zchunk_t *chunk = zfile_read (file, zfile_cursize (file), 0); if (chunk) { self = zconfig_chunk_load (chunk); zchunk_destroy (&chunk); if (self) self->file = file; zfile_close (file); file = NULL; // Config tree now owns file handle } } zfile_destroy (&file); return self; }
void zproto_example_destroy (zproto_example_t **self_p) { assert (self_p); if (*self_p) { zproto_example_t *self = *self_p; // Free class properties zframe_destroy (&self->routing_id); free (self->data); if (self->aliases) zlist_destroy (&self->aliases); zhash_destroy (&self->headers); zchunk_destroy (&self->public_key); zuuid_destroy (&self->identifier); zframe_destroy (&self->address); zmsg_destroy (&self->content); // Free object itself free (self); *self_p = NULL; } }
void zconfig_test (bool verbose) { printf (" * zconfig: "); // @selftest // Create temporary directory for test files # define TESTDIR ".test_zconfig" zsys_dir_create (TESTDIR); zconfig_t *root = zconfig_new ("root", NULL); assert (root); zconfig_t *section, *item; section = zconfig_new ("headers", root); assert (section); item = zconfig_new ("email", section); assert (item); zconfig_set_value (item, "*****@*****.**"); item = zconfig_new ("name", section); assert (item); zconfig_set_value (item, "Justin Kayce"); zconfig_putf (root, "/curve/secret-key", "%s", "Top Secret"); zconfig_set_comment (root, " CURVE certificate"); zconfig_set_comment (root, " -----------------"); assert (zconfig_comments (root)); zconfig_save (root, TESTDIR "/test.cfg"); zconfig_destroy (&root); root = zconfig_load (TESTDIR "/test.cfg"); if (verbose) zconfig_save (root, "-"); assert (streq (zconfig_filename (root), TESTDIR "/test.cfg")); char *email = zconfig_get (root, "/headers/email", NULL); assert (email); assert (streq (email, "*****@*****.**")); char *passwd = zconfig_get (root, "/curve/secret-key", NULL); assert (passwd); assert (streq (passwd, "Top Secret")); zconfig_savef (root, "%s/%s", TESTDIR, "test.cfg"); assert (!zconfig_has_changed (root)); int rc = zconfig_reload (&root); assert (rc == 0); assert (!zconfig_has_changed (root)); zconfig_destroy (&root); // Test chunk load/save root = zconfig_new ("root", NULL); assert (root); section = zconfig_new ("section", root); assert (section); item = zconfig_new ("value", section); assert (item); zconfig_set_value (item, "somevalue"); zconfig_t *search = zconfig_locate (root, "section/value"); assert (search == item); zchunk_t *chunk = zconfig_chunk_save (root); assert (strlen ((char *) zchunk_data (chunk)) == 32); char *string = zconfig_str_save (root); assert (string); assert (streq (string, (char *) zchunk_data (chunk))); free (string); assert (chunk); zconfig_destroy (&root); root = zconfig_chunk_load (chunk); assert (root); char *value = zconfig_get (root, "/section/value", NULL); assert (value); assert (streq (value, "somevalue")); // Test config can't be saved to a file in a path that doesn't // exist or isn't writable rc = zconfig_savef (root, "%s/path/that/doesnt/exist/%s", TESTDIR, "test.cfg"); assert (rc == -1); zconfig_destroy (&root); zchunk_destroy (&chunk); // Delete all test files zdir_t *dir = zdir_new (TESTDIR, NULL); assert (dir); zdir_remove (dir, true); zdir_destroy (&dir); // @end printf ("OK\n"); }
void zfile_test (bool verbose) { printf (" * zfile: "); // @selftest zfile_t *file = zfile_new (NULL, "bilbo"); assert (streq (zfile_filename (file, "."), "bilbo")); assert (zfile_is_readable (file) == false); zfile_destroy (&file); // Create a test file in some random subdirectory file = zfile_new ("./this/is/a/test", "bilbo"); int rc = zfile_output (file); assert (rc == 0); zchunk_t *chunk = zchunk_new (NULL, 100); zchunk_fill (chunk, 0, 100); // Write 100 bytes at position 1,000,000 in the file rc = zfile_write (file, chunk, 1000000); assert (rc == 0); zchunk_destroy (&chunk); zfile_close (file); assert (zfile_is_readable (file)); assert (zfile_cursize (file) == 1000100); assert (!zfile_is_stable (file)); // Now append one byte to file from outside int handle = open ("./this/is/a/test/bilbo", O_WRONLY | O_TRUNC | O_BINARY, 0); assert (handle >= 0); rc = write (handle, "Hello, World\n", 13); assert (rc == 13); close (handle); assert (zfile_has_changed (file)); zclock_sleep (1001); assert (zfile_has_changed (file)); assert (!zfile_is_stable (file)); zfile_restat (file); assert (zfile_is_stable (file)); assert (streq (zfile_digest (file), "4AB299C8AD6ED14F31923DD94F8B5F5CB89DFB54")); // Check we can read from file rc = zfile_input (file); assert (rc == 0); chunk = zfile_read (file, 1000100, 0); assert (chunk); assert (zchunk_size (chunk) == 13); zchunk_destroy (&chunk); zfile_close (file); // Try some fun with symbolic links zfile_t *link = zfile_new ("./this/is/a/test", "bilbo.ln"); rc = zfile_output (link); assert (rc == 0); fprintf (zfile_handle (link), "./this/is/a/test/bilbo\n"); zfile_destroy (&link); link = zfile_new ("./this/is/a/test", "bilbo.ln"); rc = zfile_input (link); assert (rc == 0); chunk = zfile_read (link, 1000100, 0); assert (chunk); assert (zchunk_size (chunk) == 13); zchunk_destroy (&chunk); zfile_destroy (&link); // Remove file and directory zdir_t *dir = zdir_new ("./this", NULL); assert (zdir_cursize (dir) == 26); zdir_remove (dir, true); assert (zdir_cursize (dir) == 0); zdir_destroy (&dir); // Check we can no longer read from file assert (zfile_is_readable (file)); zfile_restat (file); assert (!zfile_is_readable (file)); rc = zfile_input (file); assert (rc == -1); zfile_destroy (&file); // @end printf ("OK\n"); }