LCOV - code coverage report
Current view: top level - flamenco/snapshot - fd_snapshot_loader.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 199 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "fd_snapshot_loader.h"
       2             : #include "fd_snapshot.h"
       3             : #include "fd_snapshot_restore.h"
       4             : #include "fd_snapshot_http.h"
       5             : 
       6             : #include <errno.h>
       7             : #include <fcntl.h>
       8             : #include <netdb.h>
       9             : #include <regex.h>
      10             : #include <unistd.h>
      11             : #include <sys/types.h>
      12             : #include <sys/socket.h>
      13             : #include <netinet/in.h>
      14             : 
      15             : struct fd_snapshot_loader {
      16             :   ulong magic;
      17             : 
      18             :   /* Source: HTTP */
      19             : 
      20             :   void *               http_mem;
      21             :   fd_snapshot_http_t * http;
      22             : 
      23             :   /* Source: File I/O */
      24             : 
      25             :   int                  snapshot_fd;
      26             :   fd_io_istream_file_t vfile[1];
      27             : 
      28             :   /* Source I/O abstraction */
      29             : 
      30             :   fd_io_istream_obj_t    vsrc;
      31             : 
      32             :   /* Zstandard decompressor */
      33             : 
      34             :   fd_zstd_dstream_t *  zstd;
      35             :   fd_io_istream_zstd_t vzstd[1];
      36             : 
      37             :   /* Tar reader */
      38             : 
      39             :   fd_tar_reader_t    tar[1];
      40             :   fd_tar_io_reader_t vtar[1];
      41             : 
      42             :   /* Downstream restore */
      43             : 
      44             :   fd_snapshot_restore_t * restore;
      45             : 
      46             :   /* Hash and slot numbers from filename */
      47             : 
      48             :   fd_snapshot_name_t name;
      49             : };
      50             : 
      51             : typedef struct fd_snapshot_loader fd_snapshot_loader_t;
      52             : 
      53           0 : #define FD_SNAPSHOT_LOADER_MAGIC (0xa78a73a69d33e6b1UL)
      54             : 
      55             : ulong
      56           0 : fd_snapshot_loader_align( void ) {
      57           0 :   return fd_ulong_max( alignof(fd_snapshot_loader_t), fd_zstd_dstream_align() );
      58           0 : }
      59             : 
      60             : ulong
      61           0 : fd_snapshot_loader_footprint( ulong zstd_window_sz ) {
      62           0 :   ulong l = FD_LAYOUT_INIT;
      63           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_snapshot_loader_t), sizeof(fd_snapshot_loader_t) );
      64           0 :   l = FD_LAYOUT_APPEND( l, fd_zstd_dstream_align(),       fd_zstd_dstream_footprint( zstd_window_sz ) );
      65           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_snapshot_http_t),   sizeof(fd_snapshot_http_t) );
      66             :   /* FIXME add test ensuring zstd dstream align > alignof loader */
      67           0 :   return FD_LAYOUT_FINI( l, fd_snapshot_loader_align() );
      68           0 : }
      69             : 
      70             : fd_snapshot_loader_t *
      71             : fd_snapshot_loader_new( void * mem,
      72           0 :                         ulong  zstd_window_sz ) {
      73             : 
      74           0 :   if( FD_UNLIKELY( !mem ) ) {
      75           0 :     FD_LOG_WARNING(( "NULL mem" ));
      76           0 :     return NULL;
      77           0 :   }
      78             : 
      79           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_snapshot_loader_align() ) ) ) {
      80           0 :     FD_LOG_WARNING(( "unaligned mem" ));
      81           0 :     return NULL;
      82           0 :   }
      83             : 
      84           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      85           0 :   fd_snapshot_loader_t * loader   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapshot_loader_t), sizeof(fd_snapshot_loader_t) );
      86           0 :   void *                 zstd_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_zstd_dstream_align(),       fd_zstd_dstream_footprint( zstd_window_sz ) );
      87           0 :   void *                 http_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapshot_http_t),   sizeof(fd_snapshot_http_t) );
      88           0 :   FD_SCRATCH_ALLOC_FINI( l, fd_snapshot_loader_align() );
      89             : 
      90           0 :   fd_memset( loader, 0, sizeof(fd_snapshot_loader_t) );
      91           0 :   loader->http_mem = http_mem;
      92           0 :   loader->zstd     = fd_zstd_dstream_new( zstd_mem, zstd_window_sz );
      93             : 
      94           0 :   FD_COMPILER_MFENCE();
      95           0 :   loader->magic = FD_SNAPSHOT_LOADER_MAGIC;
      96           0 :   FD_COMPILER_MFENCE();
      97             : 
      98           0 :   return loader;
      99           0 : }
     100             : 
     101             : void *
     102           0 : fd_snapshot_loader_delete( fd_snapshot_loader_t * loader ) {
     103             : 
     104           0 :   if( FD_UNLIKELY( !loader ) ) return NULL;
     105             : 
     106           0 :   if( FD_UNLIKELY( loader->magic != FD_SNAPSHOT_LOADER_MAGIC ) ) {
     107           0 :     FD_LOG_WARNING(( "invalid magic" ));
     108           0 :     return NULL;
     109           0 :   }
     110             : 
     111           0 :   fd_zstd_dstream_delete   ( loader->zstd  );
     112           0 :   fd_tar_io_reader_delete  ( loader->vtar  );
     113           0 :   fd_io_istream_zstd_delete( loader->vzstd );
     114           0 :   fd_io_istream_file_delete( loader->vfile );
     115           0 :   fd_snapshot_http_delete  ( loader->http  );
     116           0 :   fd_tar_reader_delete     ( loader->tar   );
     117           0 :   fd_zstd_dstream_delete   ( loader->zstd  );
     118             : 
     119           0 :   if( loader->snapshot_fd>=0 ) {
     120           0 :     if( FD_UNLIKELY( 0!=close( loader->snapshot_fd ) ) )
     121           0 :       FD_LOG_WARNING(( "close(%d) failed (%d-%s)", loader->snapshot_fd, errno, fd_io_strerror( errno ) ));
     122           0 :     loader->snapshot_fd = -1;
     123           0 :   }
     124             : 
     125           0 :   FD_COMPILER_MFENCE();
     126           0 :   loader->magic = 0UL;
     127           0 :   FD_COMPILER_MFENCE();
     128             : 
     129           0 :   return loader;
     130           0 : }
     131             : 
     132             : fd_snapshot_loader_t *
     133             : fd_snapshot_loader_init( fd_snapshot_loader_t *    d,
     134             :                          fd_snapshot_restore_t *   restore,
     135             :                          fd_snapshot_src_t const * src,
     136           0 :                          ulong                     base_slot ) {
     137             : 
     138           0 :   d->restore = restore;
     139             : 
     140           0 :   switch( src->type ) {
     141           0 :   case FD_SNAPSHOT_SRC_FILE:
     142           0 :     d->snapshot_fd = open( src->file.path, O_RDONLY );
     143           0 :     if( FD_UNLIKELY( d->snapshot_fd<0 ) ) {
     144           0 :       FD_LOG_WARNING(( "open(%s) failed (%d-%s)", src->file.path, errno, fd_io_strerror( errno ) ));
     145           0 :       return NULL;
     146           0 :     }
     147             : 
     148           0 :     if( FD_UNLIKELY( !fd_snapshot_name_from_cstr( &d->name, src->file.path, base_slot ) ) ) {
     149           0 :       return NULL;
     150           0 :     }
     151             : 
     152           0 :     if( FD_UNLIKELY( !fd_io_istream_file_new( d->vfile, d->snapshot_fd ) ) ) {
     153           0 :       FD_LOG_WARNING(( "Failed to create fd_io_istream_file_t" ));
     154           0 :       return NULL;
     155           0 :     }
     156             : 
     157           0 :     d->vsrc = fd_io_istream_file_virtual( d->vfile );
     158           0 :     break;
     159           0 :   case FD_SNAPSHOT_SRC_HTTP:
     160           0 :     d->http = fd_snapshot_http_new( d->http_mem, src->http.dest, src->http.ip4, src->http.port, &d->name );
     161           0 :     if( FD_UNLIKELY( !d->http ) ) {
     162           0 :       FD_LOG_WARNING(( "Failed to create fd_snapshot_http_t" ));
     163           0 :       return NULL;
     164           0 :     }
     165           0 :     fd_snapshot_http_set_path( d->http, src->http.path, src->http.path_len, base_slot );
     166           0 :     d->http->hops = (ushort)3;  /* TODO don't hardcode */
     167             : 
     168           0 :     d->vsrc = fd_io_istream_snapshot_http_virtual( d->http );
     169           0 :     break;
     170           0 :   default:
     171           0 :     __builtin_unreachable();
     172           0 :   }
     173             : 
     174             :   /* Set up the snapshot reader */
     175             : 
     176           0 :   if( FD_UNLIKELY( !fd_tar_reader_new( d->tar, &fd_snapshot_restore_tar_vt, d->restore ) ) ) {
     177           0 :     FD_LOG_WARNING(( "Failed to create fd_tar_reader_t" ));
     178           0 :     return NULL;
     179           0 :   }
     180             : 
     181           0 :   fd_zstd_dstream_reset( d->zstd );
     182             : 
     183           0 :   if( FD_UNLIKELY( !fd_io_istream_zstd_new( d->vzstd, d->zstd, d->vsrc ) ) ) {
     184           0 :     FD_LOG_WARNING(( "Failed to create fd_io_istream_zstd_t" ));
     185           0 :     return NULL;
     186           0 :   }
     187             : 
     188           0 :   if( FD_UNLIKELY( !fd_tar_io_reader_new( d->vtar, d->tar, fd_io_istream_zstd_virtual( d->vzstd ) ) ) ) {
     189           0 :     FD_LOG_WARNING(( "Failed to create fd_tar_io_reader_t" ));
     190           0 :     return NULL;
     191           0 :   }
     192             : 
     193           0 :   return d;
     194           0 : }
     195             : 
     196             : int
     197           0 : fd_snapshot_loader_advance( fd_snapshot_loader_t * dumper ) {
     198             : 
     199           0 :   fd_tar_io_reader_t * vtar = dumper->vtar;
     200             : 
     201           0 :   int untar_err = fd_tar_io_reader_advance( vtar );
     202           0 :   if( untar_err==0 )     { /* ok */ }
     203           0 :   else if( untar_err<0 ) { /* EOF */ return -1; }
     204           0 :   else {
     205           0 :     FD_LOG_WARNING(( "Failed to load snapshot (%d-%s)", untar_err, fd_io_strerror( untar_err ) ));
     206           0 :     return untar_err;
     207           0 :   }
     208             : 
     209           0 :   return 0;
     210           0 : }
     211             : 
     212             : /* fd_snapshot_src_parse determines the source from the given cstr. */
     213             : 
     214             : fd_snapshot_src_t *
     215             : fd_snapshot_src_parse( fd_snapshot_src_t * src,
     216           0 :                        char *              cstr ) {
     217             : 
     218           0 :   fd_memset( src, 0, sizeof(fd_snapshot_src_t) );
     219             : 
     220           0 :   if( 0==strncmp( cstr, "http://", 7 ) ) {
     221           0 :     static char const url_regex[] = "^http://([^:/[:space:]]+)(:[[:digit:]]+)?(/.*)?$";
     222           0 :     regex_t url_re;
     223           0 :     FD_TEST( 0==regcomp( &url_re, url_regex, REG_EXTENDED ) );
     224           0 :     regmatch_t group[4] = {0};
     225           0 :     int url_re_res = regexec( &url_re, cstr, 4, group, 0 );
     226           0 :     regfree( &url_re );
     227           0 :     if( FD_UNLIKELY( url_re_res!=0 ) ) {
     228           0 :       FD_LOG_WARNING(( "Bad URL: %s", cstr ));
     229           0 :       return NULL;
     230           0 :     }
     231             : 
     232           0 :     regmatch_t * m_hostname = &group[1];
     233           0 :     regmatch_t * m_port     = &group[2];
     234           0 :     regmatch_t * m_path     = &group[3];
     235             : 
     236           0 :     src->type = FD_SNAPSHOT_SRC_HTTP;
     237           0 :     src->http.path     = cstr + m_path->rm_so;
     238           0 :     src->http.path_len = (ulong)m_path->rm_eo - (ulong)m_path->rm_so;
     239             : 
     240             :     /* Resolve port to IPv4 address */
     241             : 
     242           0 :     if( m_port->rm_so==m_port->rm_eo ) {
     243           0 :       src->http.port = 80;
     244           0 :     } else {
     245           0 :       char port_cstr[7] = {0};
     246           0 :       strncpy( port_cstr, cstr + m_port->rm_so,
     247           0 :                fd_ulong_min( 7, (ulong)m_port->rm_eo - (ulong)m_port->rm_so ) );
     248           0 :       char * port = port_cstr + 1;
     249           0 :       char * end;
     250           0 :       ulong port_ul = strtoul( port, &end, 10 );
     251           0 :       if( FD_UNLIKELY( *end!='\0' ) ) {
     252           0 :         FD_LOG_WARNING(( "Bad port: %s", port ));
     253           0 :         return NULL;
     254           0 :       }
     255           0 :       if( FD_UNLIKELY( port_ul>65535 ) ) {
     256           0 :         FD_LOG_WARNING(( "Port out of range: %lu", port_ul ));
     257           0 :         return NULL;
     258           0 :       }
     259           0 :       src->http.port = (ushort)port_ul;
     260           0 :     }
     261             : 
     262             :     /* Resolve host to IPv4 address */
     263             : 
     264           0 :     int sep = cstr[ m_hostname->rm_eo ];
     265           0 :     cstr[ m_hostname->rm_eo ] = '\0';
     266           0 :     char * hostname = cstr + m_hostname->rm_so;
     267             : 
     268           0 :     strncpy( src->http.dest, hostname, sizeof(src->http.dest)-1 );
     269           0 :     src->http.dest[ sizeof(src->http.dest)-1 ] = '\0';
     270             : 
     271           0 :     struct sockaddr_in default_addr = {
     272           0 :       .sin_family = AF_INET,
     273           0 :       .sin_port   = htons( 80 ),
     274           0 :       .sin_addr   = { .s_addr = htonl( INADDR_ANY ) }
     275           0 :     };
     276           0 :     struct addrinfo hints = {
     277           0 :       .ai_family   = AF_INET,
     278           0 :       .ai_socktype = SOCK_STREAM,
     279           0 :       .ai_addr     = fd_type_pun( &default_addr ),
     280           0 :       .ai_addrlen  = sizeof(struct sockaddr_in)
     281           0 :     };
     282           0 :     struct addrinfo * result = NULL;
     283           0 :     int lookup_res = getaddrinfo( hostname, NULL, &hints, &result );
     284           0 :     if( FD_UNLIKELY( lookup_res ) ) {
     285           0 :       FD_LOG_WARNING(( "getaddrinfo(%s) failed (%d-%s)", hostname, lookup_res, gai_strerror( lookup_res ) ));
     286           0 :       return NULL;
     287           0 :     }
     288             : 
     289           0 :     cstr[ m_hostname->rm_eo ] = (char)sep;
     290             : 
     291           0 :     for( struct addrinfo * rp = result; rp; rp = rp->ai_next ) {
     292           0 :       if( rp->ai_family==AF_INET ) {
     293           0 :         struct sockaddr_in * addr = (struct sockaddr_in *)rp->ai_addr;
     294           0 :         src->http.ip4 = addr->sin_addr.s_addr;
     295           0 :         freeaddrinfo( result );
     296           0 :         return src;
     297           0 :       }
     298           0 :     }
     299             : 
     300           0 :     FD_LOG_WARNING(( "Failed to resolve socket address for %s", hostname ));
     301           0 :     freeaddrinfo( result );
     302           0 :     return NULL;
     303           0 :   } else if( 0==strncmp( cstr, "archive:", sizeof("archive:")-1 ) ) {
     304           0 :     src->type = FD_SNAPSHOT_SRC_ARCHIVE;
     305           0 :     src->file.path = cstr + (sizeof("archive:")-1);
     306           0 :     return src;
     307           0 :   } else {
     308           0 :     src->type = FD_SNAPSHOT_SRC_FILE;
     309           0 :     src->file.path = cstr;
     310           0 :     return src;
     311           0 :   }
     312             : 
     313           0 :   __builtin_unreachable();
     314           0 : }
     315             : 
     316             : fd_snapshot_name_t const *  /* nullable */
     317           0 : fd_snapshot_loader_get_name( fd_snapshot_loader_t const * loader ) {
     318           0 :   return &loader->name;
     319           0 : }

Generated by: LCOV version 1.14