static void sub_patch_add (sub_t *self, zdir_patch_t *patch) { // Skip file creation if client already has identical file zdir_patch_digest_set (patch); if (zdir_patch_op (patch) == patch_create) { char *digest = (char *) zhash_lookup (self->cache, zdir_patch_vpath (patch) + strlen(self->path) + 1); if (digest && streq (digest, zdir_patch_digest (patch))) return; // Just skip patch for this client } // Remove any previous patches for the same file zdir_patch_t *existing = (zdir_patch_t *) zlist_first (self->client->patches); while (existing) { if (streq (zdir_patch_vpath (patch), zdir_patch_vpath (existing))) { zlist_remove (self->client->patches, existing); zdir_patch_destroy (&existing); break; } existing = (zdir_patch_t *) zlist_next (self->client->patches); } if (zdir_patch_op (patch) == patch_create) zhash_insert (self->cache, zdir_patch_digest (patch), zdir_patch_vpath (patch)); // Track that we've queued patch for client, so we don't do it twice zlist_append (self->client->patches, zdir_patch_dup (patch)); }
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); }
void zdir_patch_test (bool verbose) { printf (" * zdir_patch: "); // @selftest zfile_t *file = zfile_new (".", "bilbo"); assert (file); zdir_patch_t *patch = zdir_patch_new (".", file, patch_create, "/"); assert (patch); zfile_destroy (&file); file = zdir_patch_file (patch); assert (file); assert (streq (zfile_filename (file, "."), "bilbo")); assert (streq (zdir_patch_vpath (patch), "/bilbo")); zdir_patch_destroy (&patch); #if defined (__WINDOWS__) zsys_shutdown(); #endif // @end printf ("OK\n"); }
// -------------------------------------------------------------------------- // Self test of this class int zdir_patch_test (bool verbose) { printf (" * zdir_patch: "); // @selftest zfile_t *file = zfile_new (".", "bilbo"); zdir_patch_t *patch = zdir_patch_new (".", file, patch_create, "/"); zfile_destroy (&file); file = zdir_patch_file (patch); assert (streq (zfile_filename (file, "."), "bilbo")); assert (streq (zdir_patch_vpath (patch), "/bilbo")); zdir_patch_destroy (&patch); // @end printf ("OK\n"); return 0; }
static void get_next_patch_for_client (server_t *self, client_t *client) { // Get next patch for client if we're not doing one already if (client->patch == NULL) client->patch = (zdir_patch_t *) zlist_pop (client->patches); if (client->patch == NULL) { client->next_event = finished_event; return; } // Get virtual path from patch fmq_msg_set_filename (client->reply, zdir_patch_vpath (client->patch)); // We can process a delete patch right away if (zdir_patch_op (client->patch) == patch_delete) { fmq_msg_set_sequence (client->reply, client->sequence++); fmq_msg_set_operation (client->reply, FMQ_MSG_FILE_DELETE); client->next_event = send_delete_event; // No reliability in this version, assume patch delivered safely zdir_patch_destroy (&client->patch); } else if (zdir_patch_op (client->patch) == patch_create) { // Create patch refers to file, open that for input if needed if (client->file == NULL) { client->file = zfile_dup (zdir_patch_file (client->patch)); if (zfile_input (client->file)) { // File no longer available, skip it zdir_patch_destroy (&client->patch); zfile_destroy (&client->file); client->next_event = next_patch_event; return; } client->offset = 0; } // Get next chunk for file zchunk_t *chunk = zfile_read (client->file, CHUNK_SIZE, client->offset); assert (chunk); // Check if we have the credit to send chunk if (zchunk_size (chunk) <= client->credit) { fmq_msg_set_sequence (client->reply, client->sequence++); fmq_msg_set_operation (client->reply, FMQ_MSG_FILE_CREATE); fmq_msg_set_offset (client->reply, client->offset); fmq_msg_set_chunk (client->reply, zframe_new ( zchunk_data (chunk), zchunk_size (chunk))); client->offset += zchunk_size (chunk); client->credit -= zchunk_size (chunk); client->next_event = send_chunk_event; // Zero-sized chunk means end of file if (zchunk_size (chunk) == 0) { zfile_destroy (&client->file); zdir_patch_destroy (&client->patch); } } else client->next_event = no_credit_event; zchunk_destroy (&chunk); } }
/// // Return patch virtual file path const QString QZdirPatch::vpath () { const QString rv = QString (zdir_patch_vpath (self)); return rv; }