/* * This file is part of the Distributed Network Block Device 3 * * Copyright(c) 2011-2012 Johann Latocha * * This file may be licensed under the terms of of the * GNU General Public License Version 2 (the ``GPL''). * * Software distributed under the License is distributed * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either * express or implied. See the GPL for the specific language * governing rights and limitations. * * You should have received a copy of the GPL along with this * program. If not, go to http://www.gnu.org/licenses/gpl.html * or write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "server.h" #include "utils.h" void *dnbd3_handle_query(void *dnbd3_client) { dnbd3_client_t *client = (dnbd3_client_t *) (uintptr_t) dnbd3_client; dnbd3_request_t request; dnbd3_reply_t reply; int cork = 1; int uncork = 0; dnbd3_image_t *image = NULL; int image_file, image_cache = -1; struct in_addr alt_server; int i = 0; uint64_t map_y; char map_x, bit_mask; uint64_t todo_size = 0; uint64_t todo_offset = 0; uint64_t cur_offset = 0; uint64_t last_offset = 0; int dirty = 0; while (recv(client->sock, &request, sizeof(dnbd3_request_t), MSG_WAITALL) > 0) { reply.cmd = request.cmd; reply.size = 0; memcpy(reply.handle, request.handle, sizeof(request.handle)); pthread_spin_lock(&client->spinlock); switch (request.cmd) { case CMD_GET_SERVERS: image = dnbd3_get_image(request.vid, request.rid); if(!image) goto error; int num = (image->num_servers < NUMBER_SERVERS) ? image->num_servers : NUMBER_SERVERS; reply.vid = image->vid; reply.rid = dnbd3_get_image(request.vid, 0)->rid; reply.size = num * sizeof(struct in_addr); send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0); for (i = 0; i < num; i++) { inet_aton(image->servers[i], &alt_server); send(client->sock, (char *) &alt_server, sizeof(struct in_addr), 0); } client->image = image; image->atime = time(NULL); // TODO: check if mutex is needed break; case CMD_GET_SIZE: image = dnbd3_get_image(request.vid, request.rid); if(!image) goto error; reply.vid = image->vid; reply.rid = image->rid; reply.size = sizeof(uint64_t); send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0); send(client->sock, &image->filesize, sizeof(uint64_t), 0); image_file = open(image->file, O_RDONLY); client->image = image; image->atime = time(NULL); // TODO: check if mutex is needed if (image->cache_file) image_cache = open(image->cache_file, O_RDWR); break; case CMD_GET_BLOCK: if (image_file < 0) goto error; setsockopt(client->sock, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); reply.size = request.size; send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0); // caching is off if (!image->cache_file) { if (sendfile(client->sock, image_file, (off_t *) &request.offset, request.size) < 0) printf("ERROR: Sendfile failed (sock)\n"); setsockopt(client->sock, SOL_TCP, TCP_CORK, &uncork, sizeof(uncork)); break; } // caching is on dirty = 0; todo_size = 0; todo_offset = request.offset; cur_offset = request.offset; last_offset = request.offset + request.size; while(cur_offset < last_offset) { map_y = cur_offset >> 15; map_x = (cur_offset >> 12) & 7; // mod 8 bit_mask = 0b00000001 << (map_x); cur_offset += 4096; if ((image->cache_map[map_y] & bit_mask) != 0) // cache hit { if (todo_size != 0) // fetch missing chunks { lseek(image_cache, todo_offset, SEEK_SET); if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) < 0) printf("ERROR: Sendfile failed (cache)\n"); todo_size = 0; dirty = 1; } todo_offset = cur_offset; } else { todo_size += 4096; } } // whole request was missing if (todo_size != 0) { lseek(image_cache, todo_offset, SEEK_SET); if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) < 0) printf("ERROR: Sendfile failed (cache)\n"); dirty = 1; } if (dirty) { // set 1 in cache map for whole request cur_offset = request.offset; while(cur_offset < last_offset) { map_y = cur_offset >> 15; map_x = (cur_offset >> 12) & 7; // mod 8 bit_mask = 0b00000001 << (map_x); image->cache_map[map_y] |= bit_mask; cur_offset += 4096; } } // send data to client if (sendfile(client->sock, image_cache, (off_t *) &request.offset, request.size) < 0) printf("ERROR: Sendfile failed (net)\n"); setsockopt(client->sock, SOL_TCP, TCP_CORK, &uncork, sizeof(uncork)); break; default: printf("ERROR: Unknown command\n"); break; } pthread_spin_unlock(&client->spinlock); continue; error: printf("ERROR: Client requested an unknown image id.\n"); send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0); pthread_spin_unlock(&client->spinlock); continue; } close(client->sock); close(image_file); close(image_cache); pthread_spin_lock(&_spinlock); _dnbd3_clients = g_slist_remove(_dnbd3_clients, client); pthread_spin_unlock(&_spinlock); printf("INFO: Client %s exit\n", client->ip); free(client); pthread_exit((void *) 0); } int dnbd3_setup_socket() { int sock; struct sockaddr_in server; // Create socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) { printf("ERROR: Socket failure\n"); return -1; } memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; // IPv4 server.sin_addr.s_addr = htonl(INADDR_ANY); // Take all IPs server.sin_port = htons(PORT); // set port number // Bind to socket if (bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0) { printf("ERROR: Bind failure\n"); return -1; } // Listen on socket if (listen(sock, 100) == -1) { printf("ERROR: Listen failure\n"); return -1; } return sock; }