libftc
 Todos Classes Namespaces Arquivos Funções Variáveis Definições de Tipos Enumerações Valores enumerados Definições e Macros
ftc.cpp
Vá para a documentação deste arquivo.
1 
2 #define WIN32_LEAN_AND_MEAN
3 
4 #include "ftc.h"
5 
6 #ifdef TEC_WINDOWS
7 #include <winsock2.h>
8 #include <windows.h>
9 #else
10 #include <inttypes.h>
11 #include <arpa/inet.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netdb.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #endif
19 
20 #include <stdexcept>
21 #include <cstdlib>
22 #include <cstdio>
23 #include <cstring>
24 #include <cassert>
25 
26 #include <vector>
27 
28 namespace ftc_detail {
29 
30 #ifdef TEC_WINDOWS
31 typedef SOCKET socket_type;
32 #define TEC_INVALID_SOCKET INVALID_SOCKET
33 #define TEC_SOCKET_ERROR SOCKET_ERROR
34 #else
35 typedef int socket_type;
36 #define TEC_INVALID_SOCKET (-1)
37 #define TEC_SOCKET_ERROR (-1)
38 #endif
39 
40 namespace {
41 
42 void send(socket_type socket, const char* buffer, std::size_t size);
43 void recv(socket_type socket, char* buffer, std::size_t size);
44 
46 {
47  errno_buffer_ref(const char* buffer)
48  : buffer(buffer) {}
49  const char* buffer;
50 };
51 
52 // errno_buffer has move semantics
54 {
56  : buffer(r.buffer) {}
57  explicit errno_buffer(const char* buffer) : buffer(buffer) {}
59  : buffer(o.release())
60  {}
62  {
63  buffer = o.release();
64  return *this;
65  }
66 #ifdef TEC_WINDOWS
67  ~errno_buffer()
68  {
69  if(buffer)
70  ::LocalFree(static_cast<HLOCAL>
71  (const_cast<void*>
72  (static_cast<const void*>(buffer))));
73  }
74 #endif
75  operator errno_buffer_ref ()
76  {
77  return errno_buffer_ref(release());
78  }
79  const char* message() const { return buffer; }
80  const char* release()
81  {
82  const char* b = buffer;
83  buffer = 0;
84  return b;
85  }
86 private:
87  const char* buffer;
88 };
89 
91 {
92 #ifdef linux
93  return errno_buffer(sys_errlist[errno]);
94 #elif defined(TEC_WINDOWS)
95  const char* buffer = 0;
96  DWORD r = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
97  | FORMAT_MESSAGE_FROM_SYSTEM
98  | FORMAT_MESSAGE_IGNORE_INSERTS
99  , 0, WSAGetLastError()
100  , 0, static_cast<LPTSTR>(static_cast<void*>(&buffer))
101  , 0, 0);
102  if(buffer)
103  return errno_buffer(buffer);
104  else
105  {
106  std::abort();
107  throw -1;
108  }
109 #else
110  return errno_buffer("A system error");
111 #endif
112 }
113 
115 {
116 #ifdef TEC_WINDOWS
117  ::closesocket(s);
118 #else
119  ::close(s);
120 #endif
121 }
122 
123 struct socket
124 {
125  socket(int s)
126  : sock(s) {}
127 
129  {
130  if(sock != TEC_INVALID_SOCKET)
131  close(sock);
132  }
133 
134  int release()
135  {
136  int s = sock;
137  sock = TEC_INVALID_SOCKET;
138  return s;
139  }
140  int get() const { return sock; }
141 
142  int sock;
143 };
144 
145 void throw_exception(int status, const char* message)
146 {
147  switch(status)
148  {
149  case -1:
150  throw FailureException(message);
151  case -2:
152  throw InvalidKeyException(message);
153  case -3:
154  throw FileNotFoundException(message);
155  case -4:
156  throw NoPermissionException(message);
157  case -5:
158  throw FileLockedException(message);
159  case -6:
160  throw MaxClientsReachedException(message);
161  case -7:
162  throw FileNotOpenException(message);
163  default:
164  throw FtcException(static_cast<FtcErrorCode>(static_cast<unsigned char>(static_cast<char>(status))), message);
165  }
166 }
167 
168 void throw_exception(const char* message)
169 {
170  throw FailureException(message);
171 }
172 
174 {
175 #ifdef TEC_WINDOWS
176  char status = 0;
177  WSABUF buffers = {1, &status};
178  DWORD count = 0, flags = 0;
179  int r = WSARecv(socket, &buffers, 1, &count, &flags
180  , 0, 0);
181  if(r != 0)
182  {
185  }
186  else if(count == 0)
187  throw_exception("EOF");
188  return status;
189 #else
190  char status = 0;
191  ssize_t r = ::recv(socket, &status, 1, 0);
192  if(r == -1)
193  {
196  }
197  else if(r == 0)
198  throw_exception("EOF");
199  return status;
200 #endif
201 }
202 
203 #if defined(LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(TEC_WINDOWS)
204 void endian_swap(char* buffer, std::size_t size)
205 {
206  std::vector<char> reverse_endian(size);
207  std::copy(buffer, buffer + size, reverse_endian.rbegin());
208  std::memcpy(buffer, &reverse_endian[0], size);
209 }
210 #else
211 void endian_swap(char*, std::size_t) {}
212 #endif
213 
214 unsigned long long return_long_long_command(socket_type socket, char command)
215 {
216  // This is a compile-time assertion that unsigned long long is 8 bytes
217  char v[sizeof(unsigned long long) == 8?1:0];
218  (void)v;
219 
220  send(socket, &command, 1);
221 
222  char recv_buffer[8];
223  recv(socket, recv_buffer, sizeof(recv_buffer));
224 
225  endian_swap(recv_buffer, 8);
226 
227  unsigned long long return_value;
228  std::memcpy(&return_value, recv_buffer
229  , (std::min)(sizeof(return_value),sizeof(recv_buffer)));
230  return return_value;
231 }
232 
233 void send(socket_type socket, const char* buffer, std::size_t size)
234 {
235  std::size_t total = 0;
236  while(total != size)
237  {
238 #ifdef TEC_WINDOWS
239  WSABUF buffers = {size, const_cast<char*>(buffer)};
240  DWORD count = 0;
241  int r = WSASend(socket, &buffers, 1, &count, 0, 0, 0);
242  if(r != 0)
243  {
246  }
247  else if(count == 0)
248  throw_exception("EOF");
249  total += count;
250 #else
251  ssize_t r = 0;
252  do
253  {
254  r = ::send(socket, &buffer[total], size-total, 0);
255  } while(r == -1 && errno == EINTR);
256  if(r == -1)
257  {
260  }
261  else if(r == 0)
262  throw_exception("EOF");
263  total += r;
264 #endif
265  }
266 }
267 
268 void recv(socket_type socket, char* buffer, std::size_t size)
269 {
270  std::size_t total = 0;
271  while(total != size)
272  {
273 #ifdef TEC_WINDOWS
274  WSABUF buffers = {size, buffer};
275  DWORD count = 0;
276  DWORD flags = 0;
277  int r = ::WSARecv(socket, &buffers, 1, &count, &flags, 0, 0);
278  if(r != 0)
279  {
282  }
283  else if(count == 0)
284  throw_exception("EOF");
285  total += count;
286 #else
287  ssize_t r = 0;
288  do
289  {
290  r = ::recv(socket, &buffer[total], size-total, 0);
291  } while(r == -1 && errno == EINTR);
292  if(r == -1)
293  {
296  }
297  else if(r == 0)
299  total += r;
300 #endif
301  }
302 }
303 
304 
305 }
306 
307 }
308 
309 ftc::ftc(const char* id, const char id_size, bool writable, const char* host
310  , unsigned short port, const char* accessKey, const char key_size)
311  : hostname(host)
312  , tcp_port(port)
313  , identifier (id)
314  , identifier_size (id_size)
315  , sock(TEC_INVALID_SOCKET)
316  , writable(writable)
317  , is_open(false)
318  , access_key(accessKey)
319  , access_key_size(key_size)
320 {
321 }
322 
323 void ftc::open(bool readonly)
324 {
325  if(!writable && !readonly)
326  throw NoPermissionException("Permission set in constructor");
327 
328  writable = !readonly;
329 
330 #ifdef TEC_WINDOWS
331  WORD wVersionRequested = MAKEWORD(2,2);
332  WSADATA wsaData;
333  int err = WSAStartup(wVersionRequested, &wsaData);
334  if(err != 0)
335  {
336  ftc_detail::errno_buffer b = ftc_detail::errno_message();
337  ftc_detail::throw_exception(b.message());
338  }
339 #endif
340  ftc_detail::socket s(::socket(AF_INET, SOCK_STREAM, 0));
341  if(s.get() == TEC_INVALID_SOCKET)
342  {
343  ftc_detail::errno_buffer b = ftc_detail::errno_message();
344  ftc_detail::throw_exception(b.message());
345  }
346 
347  hostent* h = gethostbyname(hostname);
348 
349  if(!h)
350  {
351  ftc_detail::errno_buffer b = ftc_detail::errno_message();
352  ftc_detail::throw_exception(b.message());
353  }
354 
355  ::sockaddr_in addr =
356  {AF_INET, htons(tcp_port)}; // htons can't be qualified, because it can be a macro
357 
358  std::memcpy(&addr.sin_addr.s_addr, h->h_addr_list[0], sizeof(addr.sin_addr.s_addr));
359 
360  if(::connect(s.get(), static_cast<struct sockaddr*>(static_cast<void*>(&addr))
361  , sizeof addr) == TEC_SOCKET_ERROR)
362  {
363  ftc_detail::errno_buffer b = ftc_detail::errno_message();
364  ftc_detail::throw_exception(b.message());
365  }
366 
367  sock = s.release();
368 
369  // authenticate
370  {
371  char size = access_key_size;
372  ftc_detail::send(sock, &size, 1);
373  ftc_detail::send(sock, access_key, access_key_size);
374  int status = ftc_detail::recv_status(sock);
375  if(status != 0)
376  ftc_detail::throw_exception(status, "Authentication");
377  }
378 
379  // open
380  char buffer[2];
381  buffer[0] = writable;
382  buffer[1] = identifier_size;
383  ftc_detail::send(sock, buffer, sizeof(buffer));
384  ftc_detail::send(sock, identifier, buffer[1]);
385  int status = ftc_detail::recv_status(sock);
386  if(status != 0)
387  ftc_detail::throw_exception(status, "open");
388  is_open = true;
389 }
390 
392 {
393  if(sock != -1)
394  ftc_detail::close(sock);
395 #ifdef TEC_WINDOWS
396  WSACleanup();
397 #endif
398 }
399 
401 {
402  if(!is_open)
403  throw FileNotOpenException("file not open - close");
404 
405  char command = 2;
406  ftc_detail::send(sock, &command, sizeof(command));
407  int status = ftc_detail::recv_status(sock);
408  if(status < 0)
409  ftc_detail::throw_exception(status, "close");
410  is_open = false;
411 }
412 
414 {
415  return is_open;
416 }
417 
418 void ftc::setPosition( unsigned long long p)
419 {
420  if(!is_open)
421  throw FileNotOpenException("file not open - close");
422 
423  char buf_start[1+8] = {5};
424  std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
425  , (std::min<std::size_t>)(sizeof(p), 8u));
426  ftc_detail::endian_swap(&buf_start[1]
427  , (std::min<std::size_t>)(8u, sizeof(p)));
428  ftc_detail::send(sock, buf_start, sizeof(buf_start));
429  int status = ftc_detail::recv_status(sock);
430  if(status < 0)
431  ftc_detail::throw_exception(status, "setPosition");
432 }
433 
434 void ftc::setReadBufferSize( unsigned long long size )
435 {
436 }
437 
438 unsigned long long ftc::getPosition()
439 {
440  if(!is_open)
441  throw FileNotOpenException("file not open - close");
442  return ftc_detail::return_long_long_command(sock, 4);
443 }
444 
445 unsigned long long ftc::getReadBufferSize()
446 {
447  return 0;
448 }
449 
450 void ftc::setSize(unsigned long long s)
451 {
452  if(!is_open)
453  throw FileNotOpenException("file not open - close");
454  else if(!writable)
455  throw NoPermissionException("mutable operation on read-only file");
456  char buf_start[1+8] = {3};
457  std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&s))
458  , (std::min<std::size_t>)(sizeof(s), 8u));
459  ftc_detail::endian_swap(&buf_start[1]
460  , (std::min<std::size_t>)(8u, sizeof(s)));
461  ftc_detail::send(sock, buf_start, sizeof(buf_start));
462  int status = ftc_detail::recv_status(sock);
463  if(status < 0)
464  ftc_detail::throw_exception(status, "setSize");
465 }
466 
467 unsigned long long ftc::getSize() const
468 {
469  if(!is_open)
470  throw FileNotOpenException("file not open - close");
471  return ftc_detail::return_long_long_command(sock, 6);
472 }
473 
474 unsigned long long ftc::read(char* data, unsigned long long size, unsigned long long p)
475 {
476  if(!is_open)
477  throw FileNotOpenException("file not open - close");
478  char buf_start[1+8+8] = {7};
479  std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
480  , (std::min<std::size_t>)(sizeof(p), sizeof(buf_start)-1));
481  std::memcpy(&buf_start[1+8], static_cast<const char*>(static_cast<const void*>(&size))
482  , (std::min<std::size_t>)(sizeof(size), sizeof(buf_start)-1));
483  ftc_detail::endian_swap(&buf_start[1]
484  , (std::min<std::size_t>)(8u, sizeof(p)));
485  ftc_detail::endian_swap(&buf_start[1+8]
486  , (std::min<std::size_t>)(8u, sizeof(p)));
487  ftc_detail::send(sock, buf_start, sizeof(buf_start));
488 
489  char* current = data;
490  char* last = data + size;
491  while(current != last)
492  {
493 #ifdef TEC_WINDOWS
494  WSABUF buffers = {last-current, current};
495  DWORD count = 0, flags = 0;
496  bool retry = false;
497  int r = 0;
498  do
499  {
500  r = ::WSARecv(sock, &buffers, 1, &count, &flags, 0, 0);
501  if(r == -1 && WSAGetLastError() == WSAENOBUFS
502  && buffers.len > ftc_detail::win32_minimum_buffer_size*2)
503  {
504  buffers.len /= 2;
505  retry = true;
506  }
507  else
508  retry = false;
509  } while(retry);
510  if(r != 0)
511  {
512  ftc_detail::errno_buffer b = ftc_detail::errno_message();
513  ftc_detail::throw_exception(b.message());
514  }
515  else if(count == 0)
516  return current - data;
517  current += count;
518 #else
519  ssize_t r = 0;
520  do
521  {
522  r = ::recv(sock, current, last-current, 0);
523  } while(r == -1 && errno == EINTR);
524  if(r == -1)
525  {
526  ftc_detail::errno_buffer b = ftc_detail::errno_message();
527  ftc_detail::throw_exception(b.message());
528  }
529  else if(r == 0)
530  return current - data;
531  current += r;
532 #endif
533  assert(current <= last);
534  }
535 
536  return current-data;
537 }
538 
539 unsigned long long ftc::write(char const* data, unsigned long long size, unsigned long long p)
540 {
541  if(!is_open)
542  throw FileNotOpenException("file not open - close");
543  else if(!writable)
544  throw NoPermissionException("mutable operation on read-only file");
545  char buf_start[1+8+8] = {8};
546  std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
547  , (std::min<std::size_t>)(sizeof(p), 8u));
548  std::memcpy(&buf_start[1+8], static_cast<const char*>(static_cast<const void*>(&size))
549  , (std::min<std::size_t>)(sizeof(size), 8u));
550  ftc_detail::endian_swap(&buf_start[1]
551  , (std::min<std::size_t>)(8u, sizeof(p)));
552  ftc_detail::endian_swap(&buf_start[1+8]
553  , (std::min<std::size_t>)(8u, sizeof(size)));
554  ftc_detail::send(sock, buf_start, sizeof(buf_start));
555 
556  const char* current = data
557  , *last = data + size;
558  while(current != last)
559  {
560 #ifdef TEC_WINDOWS
561  WSABUF buffers = {last-current, const_cast<char*>(current)};
562  DWORD count = 0;
563  bool retry = false;
564  int r = 0;
565  do
566  {
567  r = ::WSASend(sock, &buffers, 1, &count, 0, 0, 0);
568  if(r == -1 && WSAGetLastError() == WSAENOBUFS
569  && buffers.len > ftc_detail::win32_minimum_buffer_size*2)
570  {
571  buffers.len /= 2;
572  retry = true;
573  }
574  else
575  retry = false;
576  } while(retry);
577  if(r != 0)
578  {
579  ftc_detail::errno_buffer b = ftc_detail::errno_message();
580  ftc_detail::throw_exception(b.message());
581  }
582  else if(count == 0)
583  return current - data;
584  current += count;
585 #else
586  ssize_t r = 0;
587  do
588  {
589  r = ::send(sock, current, last-current, 0);
590  } while(r == -1 && errno == EINTR);
591  if(r < 0)
592  {
593  ftc_detail::errno_buffer b = ftc_detail::errno_message();
594  ftc_detail::throw_exception(b.message());
595  }
596  else if(r == 0)
597  return current - data;
598  current += r;
599 #endif
600  assert(current <= last);
601  }
602 
603  int status = ftc_detail::recv_status(sock);
604  if(status < 0)
605  ftc_detail::throw_exception(status, "write");
606 
607  return current-data;
608 }
609 
610 unsigned long long ftc::transferTo(unsigned long long position, unsigned long long nbytes, FILE* fd, char* buffer)
611 {
612  if(!is_open)
613  throw FileNotOpenException("file not open - close");
614 
615  if(nbytes == 0)
616  return 0;
617 
618  std::vector<char> new_buffer((std::min)(1024u*1024u, static_cast<unsigned int>(nbytes)));
619 
620  unsigned long long bytes_to_read = nbytes;
621 
622  while(bytes_to_read != 0)
623  {
624  unsigned long long r = read(&new_buffer[0], new_buffer.size(), position);
625  if(r == 0)
626  return nbytes - bytes_to_read;
627  position += r;
628  bytes_to_read -= r;
629 #ifdef _MSC_VER
630 #pragma warning(push)
631 #pragma warning(disable:4244) // warning C4244: 'argument' :
632  // conversion from 'unsigned __int64' to 'size_t', possible loss of data
633 #endif
634  fwrite(&new_buffer[0], r, 1, fd);
635 #ifdef _MSC_VER
636 #pragma warning(pop)
637 #endif
638  }
639 
640  return nbytes;
641 }
642 
643 
TEC_FTC_DECL bool isOpen()
Indica se o arquivo está aberto.
Definition: ftc.cpp:413
#define TEC_INVALID_SOCKET
Definition: ftc.cpp:36
Essa exceção é lançada quando uma falha desconhecida aconteceu.
Essa exceção é lançada quando o servidor atingiu o número máximo de clientes.
Definition: ftc_exception.h:96
TEC_FTC_DECL unsigned long long write(const char *data, unsigned long long nbytes, unsigned long long position)
Escreve uma quantidade de bytes no arquivo.
Definition: ftc.cpp:539
int recv_status(socket_type socket)
Definition: ftc.cpp:173
TEC_FTC_DECL unsigned long long getSize() const
Retorna o tamanho atual do arquivo.
Definition: ftc.cpp:467
void endian_swap(char *, std::size_t)
Definition: ftc.cpp:211
TEC_FTC_DECL void open(bool readonly)
Abre o arquivo remoto.
Definition: ftc.cpp:323
TEC_FTC_DECL ftc(const char *id, const char id_size, bool writable, const char *host, unsigned short port, const char *accessKey, const char key_size)
Construtor.
Definition: ftc.cpp:309
Essa exceção é lançada quando o arquivo está reservado para acesso exclusivo.
#define TEC_SOCKET_ERROR
Definition: ftc.cpp:37
TEC_FTC_DECL void setPosition(unsigned long long position)
Posiciona o cursor de leitura no arquivo.
Definition: ftc.cpp:418
Essa exceção é lançada quando uma operação falha por causa de falta de permissões para realizar-la...
TEC_FTC_DECL unsigned long long read(char *data, unsigned long long nbytes, unsigned long long position)
Le uma quantidade de bytes a partir de uma dada posição.
Definition: ftc.cpp:474
errno_buffer & operator=(errno_buffer &o)
Definition: ftc.cpp:61
Define a excecao base da biblioteca FTC.
Definition: ftc_exception.h:28
void throw_exception(const char *message)
Definition: ftc.cpp:168
TEC_FTC_DECL ~ftc()
Destrutor.
Definition: ftc.cpp:391
void recv(socket_type socket, char *buffer, std::size_t size)
Definition: ftc.cpp:268
TEC_FTC_DECL unsigned long long transferTo(unsigned long long position, unsigned long long nbytes, FILE *fd, char *buffer)
Transfere os dados do arquivo remoto diretamente para um arquivo local.
Definition: ftc.cpp:610
TEC_FTC_DECL unsigned long long getReadBufferSize()
Retorna o tamanho atual do buffer de leitura.
Definition: ftc.cpp:445
TEC_FTC_DECL void setSize(unsigned long long size)
Define o tamanho do arquivo. Pode ser usado para alocar um espaço ou truncar o arquivo.
Definition: ftc.cpp:450
Essa exceção é lançada quando a chave de acesso utilizada na conexão é inválida.
unsigned long long return_long_long_command(socket_type socket, char command)
Definition: ftc.cpp:214
void send(socket_type socket, const char *buffer, std::size_t size)
Definition: ftc.cpp:233
Essa exceção é lançada quando o arquivo não é encontrado no servidor.
TEC_FTC_DECL unsigned long long getPosition()
Retorna a atual posição do cursor de leitura no arquivo.
Definition: ftc.cpp:438
TEC_FTC_DECL void close()
Fecha o arquivo.
Definition: ftc.cpp:400
TEC_FTC_DECL void setReadBufferSize(unsigned long long size)
Define o tamanho do buffer de leitura utilizado na leitura do arquivo.
Definition: ftc.cpp:434
int socket_type
Definition: ftc.cpp:35
Essa exceção é lançada quando o metodo close é chamado sem que o arquivo remoto esteja aberto...
Definition: ftc_exception.h:81