1 /*===========================================================================
2  *
3  *                            PUBLIC DOMAIN NOTICE
4  *               National Center for Biotechnology Information
5  *
6  *  This software/database is a "United States Government Work" under the
7  *  terms of the United States Copyright Act.  It was written as part of
8  *  the author's official duties as a United States Government employee and
9  *  thus cannot be copyrighted.  This software/database is freely available
10  *  to the public for use. The National Library of Medicine and the U.S.
11  *  Government have not placed any restriction on its use or reproduction.
12  *
13  *  Although all reasonable efforts have been taken to ensure the accuracy
14  *  and reliability of the software and data, the NLM and the U.S.
15  *  Government do not and cannot warrant the performance or results that
16  *  may be obtained by using this software or data. The NLM and the U.S.
17  *  Government disclaim all warranties, express or implied, including
18  *  warranties of performance, merchantability or fitness for any particular
19  *  purpose.
20  *
21  *  Please cite the author in any work or product based on this material.
22  *
23  * ===========================================================================
24  */
25 
26 #include <kfs/extern.h>
27 
28 struct KCacheTee2File;
29 #define KFILE_IMPL struct KCacheTee2File
30 #include <kfs/impl.h>
31 
32 #include <klib/rc.h>
33 #include <klib/log.h>
34 #include <klib/text.h>
35 #include <klib/printf.h>
36 #include <klib/time.h>
37 
38 #include <kfs/recorder.h>
39 #include "poolpages.h"
40 #include <kproc/queue.h>
41 #include <kproc/timeout.h>
42 #include <kfs/cachetee2file.h>
43 
44 #include <kfs/defs.h>
45 
46 #include <sysalloc.h>
47 #include <stdlib.h>
48 
49 /*--------------------------------------------------------------------------
50  layout of local file:
51 
52  CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC MMMMMMMMMMMMMM SS BB
53 
54  C ... file content in valid/invalid blocks
55  M ... bitmap of blocks bytes = ( ( ( content-size / block-size ) + 1 ) / 8 ) + 1 )
56  S ... size of content ( uint64_t ) 8 bytes
57  B ... used blocksize  ( uint32_t ) 4 bytes
58  */
59 
60 /*--------------------------------------------------------------------------
61  * KCacheTeeFile2
62  */
63 
64 #define CACHE_TEE_DEFAULT_BLOCKSIZE ( 128 * 1024 )
65 #define CACHE_TEE_MIN_BLOCKSIZE ( 16 * 1024 )
66 #define READ_BLOCK_SIZE_TRYS 3
67 #define READ_CONTENT_SIZE_TRYS 3
68 
69 typedef struct KCacheTee2File
70 {
71     KFile dad;
72     const KFile * wrapped;                  /* the file we are wrapping */
73     KFile * cache;                          /* the cache-file */
74     KDirectory * dir;                       /* we have to store a KDirectory because we need it at closing the file,
75                                                where we test for promoting the cache */
76 
77     uint64_t wrapped_size;                  /* the size of the wrapped file */
78     uint64_t cache_size;                    /* the size of the local cache file ( remote_size + bitmap + tail ) */
79     uint64_t block_count;                   /* how many blocks do we need to cache the remote file ( last block may be shorter ) */
80     uint64_t bitmap_bytes;                  /* how many bytes do we need to store the bitmap */
81 
82     atomic32_t * bitmap;                    /* the bitmap: each bit represents one block */
83 
84     KQueue * scratch_pool;                  /* this is necessary to make KCacheTeeFile thread-safe! */
85     struct ThePool * pool;                  /* have a cache to answer from RAM! */
86 
87     uint32_t block_size;                    /* how big is a block ( aka 1 bit in the bitmap )*/
88 
89     bool read_only;
90     char cache_path [ 1 ];                  /* stores the path to the local cache, for eventual promoting at close */
91 } KCacheTee2File;
92 
93 
94 #if __BYTE_ORDER == __LITTLE_ENDIAN
95 #define SWAP_FN(val) (val)
96 #else
97 #define SWAP_FN(val) \
98     (((val)>>24)&0xff) | /* move byte 3 to byte 0 */ \
99     (((val)<<8)&0xff0000) | /* move byte 1 to byte 2 */ \
100     (((val)>>8)&0xff00) | /* move byte 2 to byte 1 */ \
101     (((val)<<24)&0xff000000) /* byte 0 to byte 3 */
102 #endif
103 #define GEN_BIT_NR_MASK_ROW(i) SWAP_FN( 1 << ( (i) * 4 ) ), SWAP_FN( 1 << ( (i) * 4 + 1 ) ), SWAP_FN( 1 << ( (i) * 4 + 2 ) ), SWAP_FN( 1 << ( (i) * 4 + 3 ) )
104 
105 const uint32_t BitNr2Mask_2[ 32 ] =
106 {
107     GEN_BIT_NR_MASK_ROW(0),
108     GEN_BIT_NR_MASK_ROW(1),
109     GEN_BIT_NR_MASK_ROW(2),
110     GEN_BIT_NR_MASK_ROW(3),
111     GEN_BIT_NR_MASK_ROW(4),
112     GEN_BIT_NR_MASK_ROW(5),
113     GEN_BIT_NR_MASK_ROW(6),
114     GEN_BIT_NR_MASK_ROW(7)
115 };
116 #undef SWAP_FN
117 #undef GEN_BIT_NR_MASK_ROW
118 
119 #define IS_CACHE_BIT( CacheFile, Block_Nr ) \
120     ( ( atomic32_read ( & ( CacheFile )->bitmap[ (Block_Nr) >> 5 ] ) & BitNr2Mask_2[ (Block_Nr) & 31 ] ) > 0 )
121 
122 #define IS_BITMAP_BIT( BitMap, Block_Nr ) \
123     ( ( atomic32_read ( & ( BitMap )[ (Block_Nr) >> 5 ] ) & BitNr2Mask_2[ (Block_Nr) & 31 ] ) > 0 )
124 
125 #define BITS_2_BYTES( BitCount ) ( ( ( BitCount ) + 7 ) >> 3 )
126 #define SIZE_2_BLOCK_COUNT( Number_Of_Bytes, Block_Size ) ( ( ( Number_Of_Bytes ) + ( Block_Size ) - 1 ) / ( Block_Size ) )
127 
128 /* called from: initialize_existing_cache_tee(), initialize_new_cache_tee(), GetCacheTee2FileCompleteness() */
create_bitmap_buffer(atomic32_t ** bitmap,uint64_t bitmap_bytes)129 static rc_t create_bitmap_buffer( atomic32_t ** bitmap, uint64_t bitmap_bytes )
130 {
131     rc_t rc = 0;
132     *bitmap = calloc ( sizeof **bitmap, ( bitmap_bytes + sizeof ** bitmap - 1 ) / sizeof ** bitmap );
133     if ( *bitmap == NULL )
134     {
135         rc = RC ( rcFS, rcFile, rcConstructing, rcMemory, rcExhausted );
136         LOGERR( klogErr, rc, "init local bitmap-area" );
137     }
138     return rc;
139 }
140 
141 /* called from: IsCacheFileComplete() and IsCacheTeeComplete() */
is_bitmap_full(const atomic32_t * bitmap,uint64_t bitmap_bytes,uint64_t block_count)142 static bool is_bitmap_full( const atomic32_t * bitmap, uint64_t bitmap_bytes, uint64_t block_count )
143 {
144     uint64_t bitmap_word;
145     const uint64_t bitmap_words_minus_one = ( ( bitmap_bytes + sizeof * bitmap - 1 ) >> 2 ) - 1;
146     for( bitmap_word = 0; bitmap_word < bitmap_words_minus_one; ++ bitmap_word )
147     {
148         if ( ~ atomic32_read ( & bitmap [ bitmap_word ] ) != 0 )
149             return false;
150     }
151 
152     {
153         uint64_t block_id = ( bitmap_word << 5 );
154         while ( block_id < block_count )
155         {
156             bool block_cached = IS_BITMAP_BIT( bitmap, block_id );
157             if ( !block_cached )
158                 return false;
159             ++block_id;
160         }
161     }
162     return true;
163 }
164 
165 /* called from: initialize_new_cache_tee() */
write_bitmap_and_tail(struct KFile * f,const atomic32_t * bitmap,uint64_t to_wrap_size,uint64_t bitmap_bytes,uint32_t blocksize)166 static rc_t write_bitmap_and_tail( struct KFile * f,
167                                    const atomic32_t * bitmap,
168                                    uint64_t to_wrap_size,
169                                    uint64_t bitmap_bytes,
170                                    uint32_t blocksize )
171 {
172     size_t written;
173     uint64_t pos = to_wrap_size;
174 
175     /* write the bitmap out */
176     rc_t rc = KFileWriteAll ( f, pos, ( const void * ) bitmap, bitmap_bytes, &written );
177     if ( rc == 0 && written != bitmap_bytes )
178     {
179         rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
180     }
181     if ( rc != 0 )
182     {
183         LOGERR( klogErr, rc, "cannot write bitmap" );
184     }
185 
186     /* write the size of the to be wrapped file out */
187     if ( rc == 0 )
188     {
189         pos += written;
190         rc = KFileWriteAll ( f, pos, &to_wrap_size, sizeof ( to_wrap_size ), &written );
191         if ( rc == 0 && written != sizeof ( to_wrap_size ) )
192         {
193             rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
194         }
195         if ( rc != 0  )
196         {
197             LOGERR( klogErr, rc, "cannot write size of to be wrapped file" );
198         }
199     }
200 
201     /* write the blocksize at the end of the cache-file */
202     if ( rc == 0 )
203     {
204         pos += written;
205         rc = KFileWriteAll ( f, pos, &blocksize, sizeof ( blocksize ), &written );
206         if ( rc == 0 && written != sizeof ( blocksize ) )
207         {
208             rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
209         }
210         if ( rc != 0  )
211         {
212             LOGERR( klogErr, rc, "cannot write blocksize" );
213         }
214     }
215     return rc;
216 }
217 
218 /* called from:  initialize_existing_cache_tee(), TruncateCacheTee2File(),
219     GetCacheTee2FileCompleteness(), GetCacheTee2FileTruncatedSize() */
read_block_size(const struct KFile * self,uint64_t cache_size,uint32_t * block_size)220 static rc_t read_block_size ( const struct KFile * self, uint64_t cache_size, uint32_t *block_size )
221 {
222     if ( cache_size >= sizeof *block_size )
223     {
224         uint64_t pos = cache_size - ( sizeof *block_size );
225         int num_try = READ_BLOCK_SIZE_TRYS;
226         rc_t rc;
227 
228         while ( true )
229         {
230             size_t num_read;
231             rc =  KFileRead ( self, pos, block_size, sizeof *block_size, &num_read );
232             if ( rc == 0 )
233             {
234                 if ( num_read != sizeof *block_size )
235                     rc = SILENT_RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
236             }
237 
238             if ( rc == 0 && *block_size != 0 )
239                 // we are done
240                 return 0;
241 
242             if ( --num_try == 0 )
243                 break;
244 
245             KSleep( 1 );
246         }
247 
248         if ( rc != 0 )
249             return rc;
250     }
251     return RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
252 }
253 
254 /* called from: initialize_existing_cache_tee(), TruncateCacheTee2File(),
255     GetCacheTee2FileCompleteness(), GetCacheTee2FileTruncatedSize() */
read_content_size(const struct KFile * self,uint64_t cache_size,uint64_t * content_size)256 static rc_t read_content_size ( const struct KFile * self, uint64_t cache_size, uint64_t *content_size )
257 {
258     if ( cache_size >= sizeof( *content_size ) + 4 )
259     {
260         uint64_t pos = ( cache_size - 4 ) - sizeof( *content_size );
261         int num_try = READ_CONTENT_SIZE_TRYS;
262         rc_t rc;
263 
264         while ( true )
265         {
266             size_t num_read;
267             rc =  KFileRead ( self, pos, content_size, sizeof *content_size, &num_read );
268             if ( rc == 0 )
269             {
270                 if ( num_read != sizeof *content_size )
271                     rc = SILENT_RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
272             }
273 
274             if ( rc == 0 && *content_size != 0 )
275             {
276                 if ( *content_size < cache_size )
277                     return 0;
278                 else
279                     return RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
280             }
281 
282             if ( --num_try == 0 )
283                 break;
284 
285             KSleep( 1 );
286         }
287 
288         if ( rc != 0 )
289             return rc;
290     }
291     return RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
292 }
293 
294 /* called from: TruncateCacheTee2File(), GetCacheTee2FileCompleteness(), GetCacheTee2FileTruncatedSize() */
verify_file_structure(const uint64_t cache_size,const uint32_t block_size,const uint64_t content_size,uint64_t * block_count,uint64_t * bitmap_bytes)295 static rc_t verify_file_structure ( const uint64_t cache_size, const uint32_t block_size, const uint64_t content_size,
296                                    uint64_t * block_count, uint64_t * bitmap_bytes )
297 {
298     rc_t rc = 0;
299     uint64_t expected_size;
300 
301     *block_count = SIZE_2_BLOCK_COUNT( content_size, block_size );
302     *bitmap_bytes = BITS_2_BYTES( *block_count );
303 
304     /* check if the values 'content-size' and 'block_size' result in the currect real file size */
305     expected_size = content_size + *bitmap_bytes + sizeof ( cache_size ) + sizeof ( block_size );
306     if ( expected_size != cache_size )
307         rc = RC ( rcFS, rcFile, rcValidating, rcParam, rcInvalid );
308     return rc;
309 }
310 
311 static rc_t switch_to_read_only ( const KCacheTee2File *cself, rc_t rc, int tag );
312 
313 /* called from: KCacheTee2FileDestroy() */
promote_cache(KCacheTee2File * self)314 static rc_t promote_cache ( KCacheTee2File * self )
315 {
316     char cache_file_name [ 4096 ];
317     char temp_file_name [ 4096 ];
318     size_t num_writ;
319     rc_t rc = string_printf ( cache_file_name, sizeof cache_file_name, &num_writ, "%s.cache", self -> cache_path );
320     if ( rc == 0 )
321         rc = string_printf ( temp_file_name, sizeof temp_file_name, &num_writ, "%s.cache.temp", self -> cache_path );
322 
323     /* (1) releaes open cache file ( windows cannot rename open files ) */
324     if ( rc == 0 )
325         rc = KFileRelease( self -> cache );
326 
327     /* (2) rename to temporary name */
328     if ( rc == 0 )
329     {
330         self -> cache = NULL;
331         rc = KDirectoryRename ( self -> dir, true, cache_file_name, temp_file_name );
332     }
333 
334     /* (3) re-open from temporary name */
335     if ( rc == 0 )
336         rc = KDirectoryOpenFileWrite( self -> dir, &self -> cache, true, "%s", temp_file_name );
337 
338     /* (4) perform truncation */
339     if ( rc == 0 )
340         rc = TruncateCacheTee2File( self -> cache );
341 
342     /* (5) release open temp. cache file ( windows cannot rename open files ) */
343     if ( rc == 0 )
344         rc = KFileRelease( self -> cache );
345 
346     /* (6) rename to final filename ( windows cannot rename open files ) */
347     if ( rc == 0 )
348     {
349         self -> cache = NULL;
350         rc = KDirectoryRename ( self -> dir, true, temp_file_name, self -> cache_path );
351     }
352 
353     /* (6) re-open from final filename */
354     if ( rc == 0 )
355         rc = KDirectoryOpenFileWrite( self -> dir, &self -> cache, true, "%s", self -> cache_path );
356 
357     return rc;
358 }
359 
360 /* called from: KCacheTee2FileDestroy() */
file_exist(KDirectory * dir,const char * filename)361 static bool file_exist( KDirectory * dir, const char * filename )
362 {
363     uint32_t pt = KDirectoryPathType ( dir, "%s", filename );
364     return ( ( pt & ~kptAlias ) == kptFile );
365 }
366 
pop_page(KQueue * buffer_pool,uint32_t timeout_millisec)367 static void * pop_page( KQueue * buffer_pool, uint32_t timeout_millisec )
368 {
369     rc_t rc;
370     void * page;
371     struct timeout_t tm;
372     TimeoutInit ( & tm, timeout_millisec );
373     rc = KQueuePop( buffer_pool, &page, &tm );
374     if ( rc != 0 )
375         page = NULL;
376     return page;
377 }
378 
379 /* helper to clean up the buffer_pool */
clean_up_pool(KQueue * buffer_pool)380 static void clean_up_pool( KQueue * buffer_pool )
381 {
382     void * pool_page;
383     while ( ( pool_page = pop_page( buffer_pool, 100 ) ) != NULL )
384     {
385         free( pool_page );
386     }
387     KQueueRelease( buffer_pool );
388 }
389 
390 /* Destroy ( entered into vtKCacheTee2File-struct )
391  */
KCacheTee2FileDestroy(KCacheTee2File * self)392 static rc_t CC KCacheTee2FileDestroy( KCacheTee2File * self )
393 {
394     bool already_promoted_by_other_instance = file_exist( self -> dir, self -> cache_path );
395 
396     if ( !self -> read_only && !already_promoted_by_other_instance )
397     {
398         bool fully_in_cache;
399         rc_t rc = IsCacheTee2FileComplete ( self -> cache, &fully_in_cache );
400         if ( rc == 0 && fully_in_cache )
401         {
402             promote_cache( self );
403         }
404     }
405 
406     if ( self->bitmap != NULL )
407         free( ( void * ) self->bitmap );
408 
409     if ( self -> scratch_pool != NULL )
410         clean_up_pool( self -> scratch_pool );
411 
412     if ( self -> pool != NULL )
413         pool_release ( self -> pool );
414 
415     KFileRelease ( self -> wrapped );
416     KFileRelease ( self -> cache );
417 
418     if ( already_promoted_by_other_instance )
419         KDirectoryRemove ( self -> dir, true, "%s.cache", self -> cache_path );
420 
421     KDirectoryRelease ( self -> dir );
422 
423     free ( self );
424     return 0;
425 }
426 
427 /* called from: KCacheTee2FileRead() */
set_bitmap(atomic32_t * bitmap,uint64_t start_block,uint64_t block_count)428 static void set_bitmap ( atomic32_t * bitmap, uint64_t start_block, uint64_t block_count )
429 {
430     uint64_t count;
431     uint64_t a_block = start_block;
432     for ( count = 0; count < block_count; count++ )
433     {
434         uint32_t old, bits;
435         const uint32_t block_bit = BitNr2Mask_2 [ a_block & 31 ];
436 
437         old = atomic32_read ( &( bitmap [ a_block >> 5 ] ) );
438         do
439         {
440             bits = old;
441             old = atomic32_test_and_set ( &( bitmap [ a_block >> 5 ] ), ( int ) ( bits | block_bit ), ( int ) bits );
442         }
443         while ( old != bits );
444         a_block++;
445     }
446 }
447 
448 /* called from: KCacheTee2FileRead()*/
write_bitmap(const KCacheTee2File * cself,uint64_t start_block,uint32_t count)449 static rc_t write_bitmap ( const KCacheTee2File *cself, uint64_t start_block, uint32_t count )
450 {
451     rc_t rc;
452     size_t written;
453     uint64_t pos;
454     size_t to_write;
455 
456     uint32_t block_word_1 = ( uint32_t ) ( start_block >> 5 );
457     uint32_t block_word_n = ( uint32_t ) ( ( start_block + count ) >> 5 );
458     uint64_t bitmap_pos = ( block_word_1 << 2 );
459     pos = cself -> wrapped_size + bitmap_pos;
460     to_write = ( block_word_n - block_word_1 + 1 ) * 4;
461 
462     // last word may go outside bitmap ranges in the file, since bitmap has 1-byte alignment in the file, but 4-byte alignment in memory
463     if ( bitmap_pos + to_write > cself->bitmap_bytes )
464         to_write = cself->bitmap_bytes - bitmap_pos;
465 
466     rc = KFileWriteAll ( cself -> cache, pos, ( const void * ) &cself->bitmap[ block_word_1 ], to_write, &written );
467     if ( rc != 0 )
468     {
469         /* it can happen that we are not able to write to the bitmap because we run out of space
470            on the local filesystem. */
471         rc = switch_to_read_only( cself, rc, 1 );
472     }
473     return rc;
474 }
475 
476 
477 typedef struct block_span
478 {
479     uint64_t first;
480     uint64_t last;
481     uint64_t count;
482 } block_span;
483 
484 typedef struct read_info
485 {
486     /* what is consecutively available at the beginning of the caller request */
487     block_span available;
488 
489     /* where the available span starts */
490     uint64_t first_block_pos;
491 
492     /* how many bytes are in the available span */
493     uint64_t bytes_to_read;
494 
495     /* is the available block_span in cache ? */
496     bool in_cache;
497 
498     struct PoolPage * pp;
499 } read_info;
500 
501 
502 /* cself ... the KCacheTee2File ( used: block_size, block_count, wrapped_size, bitmap )
503    pos ..... zero-based offset into the file ( unmodified from caller )
504    len ..... length of slice ( adjusted to not hang over the end, caller does not allow zero )
505    info .... the structure above, telling the caller what is available in cache
506 */
get_read_info(const KCacheTee2File * cself,uint64_t pos,size_t len,read_info * info)507 static void get_read_info ( const KCacheTee2File *cself, uint64_t pos, size_t len, read_info * info )
508 {
509     block_span request;
510     bool consecutiv = true;
511     bool last_block_incomplete;
512 
513     request . first = ( pos / cself -> block_size );
514     request . last  = ( ( pos + len - 1 ) / cself -> block_size );
515 
516     last_block_incomplete = ( request . last >= cself -> block_count );
517     if ( last_block_incomplete )
518         request . last = ( cself -> block_count - 1 );
519 
520     request . count = ( request . last - request . first ) + 1;
521 
522     info -> available . first = request . first;
523     info -> available . last  = request . first;
524     info -> available . count = 1;
525 
526     info -> in_cache = IS_CACHE_BIT( cself, info -> available . first );
527     while ( consecutiv && ( info -> available . count < request . count ) )
528     {
529         bool b = IS_CACHE_BIT( cself, info -> available . last + 1 );
530         consecutiv = ( info -> in_cache == b );
531         if ( consecutiv )
532         {
533             info -> available . last += 1;
534             info -> available . count += 1;
535         }
536     }
537 
538     info -> first_block_pos = ( info -> available . first * cself -> block_size );
539 
540     if ( last_block_incomplete )
541         info -> bytes_to_read = ( ( cself -> wrapped_size + 1 ) - info -> first_block_pos );
542     else
543         info -> bytes_to_read = ( info -> available . count * cself -> block_size );
544 
545     info -> pp = NULL;
546     if ( info -> in_cache )
547     {
548         info -> bytes_to_read -= ( pos - info -> first_block_pos );
549         if ( info -> bytes_to_read > len )
550             info -> bytes_to_read = len;
551 
552         pool_page_find ( cself -> pool, &( info -> pp ), pos );
553     }
554 }
555 
KCacheTee2FileRead_from_wrapped_using_page(const KCacheTee2File * cself,struct PoolPage * pp,uint64_t pos,void * buffer,size_t bsize,size_t * num_read,read_info * info)556 static rc_t KCacheTee2FileRead_from_wrapped_using_page ( const KCacheTee2File *cself, struct PoolPage * pp,
557         uint64_t pos, void * buffer, size_t bsize, size_t *num_read, read_info * info )
558 {
559     /* we have control of the page and can make as much buffer as we need to */
560     rc_t rc = pool_page_prepare( pp, info -> available . count, info -> first_block_pos );
561     if ( rc == 0 )
562     {
563         size_t from_wrapped;
564 
565         /* read from the wrapped file into the page-buffer */
566         rc = pool_page_read_from_file( pp, cself -> wrapped, &from_wrapped );
567         if ( rc == 0 )
568         {
569             size_t num_written_to_cache;
570             /* write the buffer into the local cache -file */
571             rc = pool_page_write_to_file( pp, cself -> cache, from_wrapped, &num_written_to_cache );
572             if ( rc != 0 || num_written_to_cache != from_wrapped )
573             {
574                 /* switch to read-only, because for some reason we cannot write any more...
575                    it can happen that we are not able to write to the bitmap because we run out of space
576                    on the local filesystem. */
577                 rc = switch_to_read_only( cself, rc, 2 );
578                 PLOGERR( klogInt,
579                          ( klogInt,
580                            rc,
581                           "read: $(read), written:$(written)",
582                           "read=%lu,written=%lu",
583                            from_wrapped, num_written_to_cache ) );
584             }
585             else
586             {
587                 /* set the block-bits in the bitmap... */
588                 uint32_t pp_blocks = pool_page_blocks( pp );
589                 set_bitmap ( cself -> bitmap, info -> available . first, pp_blocks );
590                 rc = write_bitmap ( cself, info -> available . first, pp_blocks );
591                 if ( rc != 0 )
592                 {
593                     /* switch to read-only, because for some reason we cannot write any more... */
594                     rc = switch_to_read_only( cself, rc, 3 );
595                 }
596             }
597         }
598         if ( rc == 0 )
599             rc = pool_page_get ( pp, pos, buffer, bsize, num_read );
600 
601     }
602     return rc;
603 }
604 
KCacheTee2FileRead_from_cache_using_page(const KCacheTee2File * cself,struct PoolPage * pp,uint64_t pos,void * buffer,size_t bsize,size_t * num_read,read_info * info)605 static rc_t KCacheTee2FileRead_from_cache_using_page ( const KCacheTee2File *cself, struct PoolPage * pp,
606         uint64_t pos, void * buffer, size_t bsize, size_t *num_read, read_info * info )
607 {
608     rc_t rc = pool_page_prepare( pp, info -> available . count, info -> first_block_pos );
609     if ( rc == 0 )
610     {
611         size_t from_cache;
612         rc = pool_page_read_from_file( pp, cself -> cache, &from_cache );
613         if ( rc == 0 )
614             rc = pool_page_get ( pp, pos, buffer, bsize, num_read );
615     }
616     return rc;
617 }
618 
619 /* */
KCacheTee2FileRead_rw_using_caller_buffer(const KCacheTee2File * cself,uint64_t pos,void * buffer,size_t * num_read,read_info * info)620 static rc_t KCacheTee2FileRead_rw_using_caller_buffer ( const KCacheTee2File *cself,
621         uint64_t pos, void *buffer, size_t *num_read, read_info * info )
622 {
623     rc_t rc;
624 
625     /* read whole blocks from the wrapped file */
626     rc = KFileReadAll ( cself -> wrapped, info -> first_block_pos, buffer, info -> bytes_to_read, num_read );
627     if ( rc == 0 )
628     {
629         /* store them in the cache-file... */
630         size_t num_written_to_cache;
631         rc = KFileWriteAll ( cself -> cache, info -> first_block_pos, buffer, *num_read, &num_written_to_cache );
632         if ( rc != 0 || num_written_to_cache != *num_read )
633         {
634             /* switch to read-only, because for some reason we cannot write any more...
635                it can happen that we are not able to write to the bitmap because we run out of space
636                on the local filesystem. */
637             rc = switch_to_read_only( cself, rc, 4 );
638         }
639         else
640         {
641             /* set the block-bits in the bitmap... */
642             set_bitmap ( cself -> bitmap, info -> available . first, info -> available . count );
643             rc = write_bitmap ( cself, info -> available . first, info -> available . count );
644             if ( rc != 0 )
645             {
646                 /* switch to read-only, because for some reason we cannot write any more... */
647                 rc = switch_to_read_only( cself, rc, 5 );
648             }
649         }
650     }
651     if ( rc == 0 )
652     {
653         /* now we have to shift the given buffer into place... */
654         uint64_t shift_by = ( pos - info -> first_block_pos );
655         if ( shift_by > 0 )
656         {
657             uint8_t * src = buffer;
658             src += shift_by;
659             *num_read = ( info -> bytes_to_read - shift_by );
660             memmove( buffer, src, *num_read );
661         }
662     }
663     return rc;
664 }
665 
KCacheTee2FileRead_rw_using_scratch_buffer(const KCacheTee2File * cself,uint64_t pos,void * buffer,size_t bsize,size_t * num_read,read_info * info)666 static rc_t KCacheTee2FileRead_rw_using_scratch_buffer ( const KCacheTee2File *cself,
667         uint64_t pos, void *buffer, size_t bsize, size_t *num_read, read_info * info )
668 {
669     rc_t rc = 0;
670     void * page = pop_page( cself -> scratch_pool, 100 );
671 
672     if ( page == NULL )
673         page = malloc ( cself -> block_size );
674     if ( page == NULL )
675         rc = RC ( rcFS, rcFile, rcConstructing, rcMemory, rcExhausted );
676     else
677     {
678         /* read one block from the wrapped file */
679         rc = KFileReadAll ( cself -> wrapped, info -> first_block_pos, page, cself -> block_size, num_read );
680         if ( rc == 0 )
681         {
682             /* store it in the cache-file... */
683             size_t num_written;
684             rc = KFileWriteAll ( cself -> cache, info -> first_block_pos, page, *num_read, &num_written );
685             if ( rc != 0 || num_written != *num_read )
686             {
687                 /* switch to read-only, because for some reason we cannot write any more...
688                    it can happen that we are not able to write to the bitmap because we run out of space
689                    on the local filesystem. */
690                 rc = switch_to_read_only( cself, rc, 6 );
691             }
692             else
693             {
694                 /* set the block-bit in the bitmap... */
695                 set_bitmap ( cself -> bitmap, info -> available . first, 1 );
696                 rc = write_bitmap ( cself, info -> available . first, 1 );
697                 if ( rc != 0 )
698                 {
699                     /* switch to read-only, because for some reason we cannot write any more... */
700                     rc = switch_to_read_only( cself, rc, 7 );
701                 }
702             }
703         }
704 
705         if ( rc == 0 )
706         {
707             /* now we have to shift the given buffer into place... */
708             uint8_t * src = page;
709             uint64_t shift_by = ( pos - info -> first_block_pos );
710             if ( shift_by > 0 )
711             {
712                 src += shift_by;
713                 *num_read = ( cself -> block_size - shift_by );
714                 memmove( buffer, src, *num_read );
715             }
716             else
717             {
718                 *num_read = bsize;
719                 memmove( buffer, src, bsize );
720             }
721         }
722 
723         if ( KQueuePush ( cself -> scratch_pool, page, NULL ) != 0 )
724             free ( page );
725     }
726     return rc;
727 }
728 
729 
730 /* new strategy: deliver either from cache or wrapped file, maybe less than requested,
731    do not use any kind of scratch-buffer to piece blocks together,
732    use the caller supplied buffer if possible, only use a scratch-buffer if
733    let the caller come back for more... */
KCacheTee2FileRead_rw(const KCacheTee2File * cself,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)734 static rc_t KCacheTee2FileRead_rw ( const KCacheTee2File *cself, uint64_t pos,
735                                     void *buffer, size_t bsize, size_t *num_read )
736 {
737     rc_t rc = 0;
738     read_info info;
739 
740     *num_read = 0;
741     if ( pos > cself -> wrapped_size )
742     {
743         /* the caller asked for data beyond the end of the file */
744         return rc;
745     }
746     else if ( ( pos + bsize ) > cself -> wrapped_size )
747     {
748         /* the caller asked for a slice of data reaching beyond the end of the file */
749         bsize = cself -> wrapped_size - pos;
750     }
751 
752     /* if the caller asked for nothing... */
753     if ( bsize == 0 )
754     {
755         return rc;
756     }
757 
758     get_read_info ( cself, pos, bsize, &info );
759 
760     if ( info . in_cache )
761     {
762         /* deliver from cache, maybe less than requested, let the caller come back for more...
763 
764         request:          |-------------------------
765         blocks:         XXXXXX======XXXXXX
766         file:     ..........................................
767 
768         or
769 
770         request:      |-----------
771         blocks:    XXXXXX======XXXXXX
772         file:     ..........................................
773 
774         or
775 
776         request:      |-----------
777         blocks:    XXXXXX======XXXXXX
778         file:     .................
779 
780         */
781         if ( info . pp != NULL )
782         {
783             /* we found it in the memory-cache! ---> GREAT! let's get it from there */
784             rc = pool_page_get ( info . pp, pos, buffer, bsize, num_read );
785         }
786         else
787         {
788             /* we did not find it in the memory-cache! ---> but we have it in the cache-file...
789                let us read it from the cache-file, but store it in the memory-cache */
790             struct PoolPage * pp;
791             if ( pool_page_find_new ( cself -> pool, &pp ) == 0 && pp != NULL )
792             {
793                 rc = KCacheTee2FileRead_from_cache_using_page ( cself, pp, pos, buffer, bsize, num_read, &info );
794                 pool_page_release ( pp );
795             }
796             else
797             {
798                 rc = KFileReadAll( cself -> cache, pos, buffer, info . bytes_to_read, num_read );
799             }
800         }
801     }
802     else
803     {
804         /* the first block is not cached ...
805            deliver from the wrapped file, maybe less than requested, store into cache, update bitmap,
806            write bitmap, let the caller come back for more...
807 
808         request:      |-------------------------
809         blocks:    XXXXXX======XXXXXX
810 
811         request:      |-----------
812         blocks:    XXXXXX======XXXXXX
813 
814         */
815 
816         struct PoolPage * pp;
817         /* first we try to allocate a pool-page ... */
818         if ( pool_page_find_new ( cself -> pool, &pp ) == 0 && pp != NULL )
819         {
820             rc = KCacheTee2FileRead_from_wrapped_using_page ( cself, pp, pos, buffer, bsize, num_read, &info );
821             pool_page_release ( pp );
822         }
823         else if ( info . bytes_to_read <= bsize )
824         {
825             /* we do have enough caller buffer to do the whole thing... */
826             rc = KCacheTee2FileRead_rw_using_caller_buffer ( cself, pos, buffer, num_read, &info );
827         }
828         else
829         {
830             /* we do NOT have enough caller buffer to do the whole thing...
831                reduce it to make it fit into the bsize
832             */
833             info . available . count = ( bsize / cself -> block_size );
834             if ( info . available . count > 0 )
835             {
836                 /* we have been given enough buffer from the caller to handle all blocks... */
837                 info . bytes_to_read = ( info . available . count * cself -> block_size );
838                 rc = KCacheTee2FileRead_rw_using_caller_buffer ( cself, pos, buffer, num_read, &info );
839             }
840             else
841             {
842                 /* we do not event have enough buffer from the caller to handle 1 block... */
843                 rc = KCacheTee2FileRead_rw_using_scratch_buffer ( cself, pos, buffer, bsize, num_read, &info );
844             }
845         }
846     }
847 
848     if ( info . pp != NULL )
849         pool_page_release ( info . pp );
850 
851     return rc;
852 }
853 
854 
855 /* this is the read-only-version of the strategy: if the first block is in cache - it is the
856    same as the r/w-version, if it is not we deliver directly from the wrapped file */
KCacheTee2FileRead_ro(const KCacheTee2File * cself,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)857 static rc_t KCacheTee2FileRead_ro ( const KCacheTee2File *cself, uint64_t pos,
858                                     void *buffer, size_t bsize, size_t *num_read )
859 {
860     rc_t rc = 0;
861     read_info info;
862 
863     *num_read = 0;
864 
865     if ( pos > cself -> wrapped_size )
866         return rc;
867     else if ( ( pos + bsize ) > cself -> wrapped_size )
868         bsize = cself -> wrapped_size - pos;
869 
870     if ( bsize == 0 )
871         return 0;
872 
873     get_read_info ( cself, pos, bsize, &info );
874 
875     if ( info . in_cache )
876     {
877         if ( info . pp != NULL )
878         {
879             /* we found it in the memory-cache! ---> GREAT! let's get it from there */
880             rc = pool_page_get ( info . pp, pos, buffer, bsize, num_read );
881         }
882         else
883         {
884             /* we did not find it in the memory-cache! ---> but we have it in the cache-file...
885                ( let's see if we can put it into memory )
886             */
887             struct PoolPage * pp;
888             if ( pool_page_find_new ( cself -> pool, &pp ) == 0 )
889             {
890                 rc = KCacheTee2FileRead_from_cache_using_page ( cself, pp, pos, buffer, bsize, num_read, &info );
891                 pool_page_release ( pp );
892             }
893             else
894                 rc = KFileReadAll( cself -> cache, pos, buffer, info . bytes_to_read, num_read );
895         }
896     }
897     else
898         rc = KFileReadAll ( cself -> wrapped, pos, buffer, info . bytes_to_read, num_read );
899 
900     if ( info . pp != NULL )
901         pool_page_release ( info . pp );
902     return rc;
903 }
904 
905 /**********************************************************************************************
906     START vt-functions
907 **********************************************************************************************/
KCacheTee2FileGetSysFile(const KCacheTee2File * self,uint64_t * offset)908 static struct KSysFile* KCacheTee2FileGetSysFile( const KCacheTee2File *self, uint64_t *offset )
909 {
910     * offset = 0;
911     return NULL;
912 }
913 
914 
KCacheTee2FileRandomAccess(const KCacheTee2File * self)915 static rc_t KCacheTee2FileRandomAccess( const KCacheTee2File *self )
916 {
917     return 0;
918 }
919 
920 
KCacheTee2FileSize(const KCacheTee2File * self,uint64_t * size)921 static rc_t KCacheTee2FileSize( const KCacheTee2File *self, uint64_t *size )
922 {
923     *size = self->wrapped_size;
924     return 0;
925 }
926 
927 
KCacheTee2FileSetSize(KCacheTee2File * self,uint64_t size)928 static rc_t KCacheTee2FileSetSize( KCacheTee2File *self, uint64_t size )
929 {
930     return RC ( rcFS, rcFile, rcUpdating, rcFile, rcReadonly );
931 }
932 
933 
KCacheTee2FileWrite(KCacheTee2File * self,uint64_t pos,const void * buffer,size_t size,size_t * num_writ)934 static rc_t KCacheTee2FileWrite( KCacheTee2File *self, uint64_t pos,
935                                 const void *buffer, size_t size, size_t *num_writ )
936 {
937     return RC ( rcFS, rcFile, rcUpdating, rcInterface, rcUnsupported );
938 }
939 
940 /**********************************************************************************************
941     END vt-functions
942 **********************************************************************************************/
943 
944 static KFile_vt_v1 vtKCacheTee2File_rw =
945 {
946     /* version 1.0 */
947     1, 0,
948 
949     /* start minor version 0 methods */
950     KCacheTee2FileDestroy,
951     KCacheTee2FileGetSysFile,
952     KCacheTee2FileRandomAccess,
953     KCacheTee2FileSize,
954     KCacheTee2FileSetSize,
955     KCacheTee2FileRead_rw,
956     KCacheTee2FileWrite
957     /* end minor version 0 methods */
958 };
959 
960 static KFile_vt_v1 vtKCacheTee2File_ro =
961 {
962     /* version 1.0 */
963     1, 0,
964 
965     /* start minor version 0 methods */
966     KCacheTee2FileDestroy,
967     KCacheTee2FileGetSysFile,
968     KCacheTee2FileRandomAccess,
969     KCacheTee2FileSize,
970     KCacheTee2FileSetSize,
971     KCacheTee2FileRead_ro,
972     KCacheTee2FileWrite
973     /* end minor version 0 methods */
974 };
975 
976 
switch_to_read_only(const KCacheTee2File * cself,rc_t rc,int tag)977 static rc_t switch_to_read_only ( const KCacheTee2File *cself, rc_t rc, int tag )
978 {
979     KFile_v1 * p1 = ( KFile_v1 * )cself;
980     KFile_vt * p2 = ( KFile_vt * )p1 -> vt;
981     p2 -> v1 = vtKCacheTee2File_ro;
982     ( ( KCacheTee2File * )cself ) -> read_only = true;
983     PLOGERR( klogInt,
984              ( klogInt,
985                rc,
986               "switch_to_read_only( tag:$(tag) )",
987               "tag=%d",
988                tag ) );
989 /*    LOGERR( klogInt, rc, "switching cache-tee-file to read-only" ); */
990     return 0;
991 }
992 
hand_out_to_wrap_file_as_tee_file(struct KFile const ** tee,struct KFile const * to_wrap)993 static rc_t hand_out_to_wrap_file_as_tee_file( struct KFile const ** tee, struct KFile const * to_wrap )
994 {
995     rc_t rc = KFileAddRef( to_wrap );
996     if ( rc != 0 )
997     {
998         LOGERR( klogErr, rc, "KFileAddRef( file to be wrapped ) failed" );
999     }
1000     else
1001     {
1002         *tee = to_wrap;
1003     }
1004     return rc;
1005 }
1006 
1007 typedef struct cache_tee_params
1008 {
1009     struct KDirectory * dir;
1010     struct KFile const * to_wrap;
1011     struct KFile * cache;
1012     uint64_t to_wrap_size;
1013     uint64_t cache_size;
1014     uint32_t block_size;
1015     size_t resolved_path_size;
1016     bool read_only;
1017     char resolved_path [ 4096 ];
1018 } cache_tee_params;
1019 
1020 
finish_tee(struct KFile const ** tee,const cache_tee_params * ctp,uint64_t block_count,uint64_t bitmap_bytes,atomic32_t * bitmap)1021 static rc_t finish_tee( struct KFile const **tee,
1022                         const cache_tee_params * ctp,
1023                         uint64_t block_count,
1024                         uint64_t bitmap_bytes,
1025                         atomic32_t * bitmap )
1026 {
1027     rc_t rc = KDirectoryAddRef ( ctp -> dir );
1028     if ( rc == 0 )
1029     {
1030         rc = KFileAddRef ( ctp -> to_wrap );
1031         if ( rc == 0 )
1032         {
1033             KQueue * q;
1034             rc = KQueueMake( &q, 32 );
1035             if ( rc == 0 )
1036             {
1037                 struct ThePool * pool;
1038                 rc = make_pool ( &pool, ctp -> block_size, 8 );
1039                 if ( rc == 0 )
1040                 {
1041                     KCacheTee2File * cf = malloc ( sizeof * cf + ctp -> resolved_path_size + 1 );
1042                     if ( cf == NULL )
1043                         rc = RC ( rcFS, rcFile, rcConstructing, rcMemory, rcExhausted );
1044                     else
1045                     {
1046                         /* now we can enter everything into the cf - struct */
1047                         cf -> wrapped = ctp -> to_wrap;
1048                         cf -> cache = ctp -> cache;
1049                         cf -> dir = ctp -> dir;
1050                         cf -> wrapped_size = ctp -> to_wrap_size;
1051                         cf -> cache_size = ctp -> cache_size;
1052                         cf -> block_count = block_count;
1053                         cf -> bitmap = bitmap;
1054                         cf -> bitmap_bytes = bitmap_bytes;
1055                         cf -> scratch_pool = q;
1056                         cf -> pool = pool;
1057                         cf -> block_size = ctp -> block_size;
1058                         cf -> read_only = ctp -> read_only;
1059 
1060                         string_copy ( cf -> cache_path,
1061                                      ctp -> resolved_path_size + 1,
1062                                      ctp -> resolved_path,
1063                                      ctp -> resolved_path_size );
1064 
1065                         if ( ctp -> read_only )
1066                         {
1067                             rc = KFileInit ( &cf -> dad,
1068                                              ( const union KFile_vt * ) &vtKCacheTee2File_ro,
1069                                              "KCacheTee2File",
1070                                              ctp -> resolved_path,
1071                                              true,
1072                                              false );
1073                         }
1074                         else
1075                         {
1076                             rc = KFileInit ( &cf -> dad,
1077                                              ( const union KFile_vt * ) &vtKCacheTee2File_rw,
1078                                              "KCacheTee2File",
1079                                              ctp -> resolved_path,
1080                                              true,
1081                                              false );
1082                         }
1083 
1084                         if ( rc != 0 )
1085                             free( ( void * ) cf );
1086                         else
1087                         {
1088                             /* the wrapper is ready to use now! */
1089                             *tee = ( const KFile * ) &cf -> dad;
1090                         }
1091                     }
1092                     if ( rc != 0 )
1093                         pool_release ( pool );
1094                 }
1095                 if ( rc != 0 )
1096                     KQueueRelease( q );
1097             }
1098             if ( rc != 0 )
1099                 KFileRelease ( ctp -> to_wrap );
1100         }
1101         if ( rc != 0 )
1102             KDirectoryRelease ( ctp -> dir );
1103     }
1104     return rc;
1105 }
1106 
initialize_existing_cache_tee(struct KFile const ** tee,cache_tee_params * ctp,bool * re_init_cache)1107 static rc_t initialize_existing_cache_tee ( struct KFile const **tee,
1108                                             cache_tee_params * ctp,
1109                                             bool * re_init_cache )
1110 {
1111     /* first we need to know the size of the cache-file */
1112     rc_t rc = KFileSize ( ctp -> cache, &( ctp -> cache_size ) );
1113     if ( rc != 0 )
1114     {
1115         LOGERR( klogErr, rc, "cannot detect size of cache-file" );
1116         *re_init_cache = true;
1117     }
1118     else
1119     {
1120         /* now let us see what is in the cache-file: what content-size was stored */
1121         uint64_t content_size;
1122         rc = read_content_size ( ctp -> cache, ctp -> cache_size, &content_size );
1123         if ( rc != 0 )
1124         {
1125             LOGERR( klogErr, rc, "cannot read content_size" );
1126             *re_init_cache = true;
1127         }
1128         else if ( content_size != ctp -> to_wrap_size )
1129         {
1130             /* if it does not match with the size of the wrapped file:
1131                we should discard the cache file and start a new one! */
1132             rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1133             PLOGERR( klogErr, ( klogErr, rc, "content-size in local file $(ls) does not match size of remote file $(rs)",
1134                                "ls=%lu,rs=%lu", content_size, ctp -> to_wrap_size ) );
1135             *re_init_cache = true;
1136         }
1137         else
1138         {
1139             uint32_t block_size;
1140             rc = read_block_size ( ctp -> cache, ctp -> cache_size, &block_size );
1141             if ( rc != 0 )
1142             {
1143                 LOGERR( klogErr, rc, "cannot read block_size" );
1144                 *re_init_cache = true;
1145             }
1146             else if ( block_size != ctp -> block_size )
1147             {
1148                 /* if it does not match with the blocksize the caller requested:
1149                    we should discard the cache file and start a new one! */
1150                 rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1151                 PLOGERR( klogErr, ( klogErr, rc, "block- in local file $(ls) does not match requested value $(rs)",
1152                                    "ls=%u,rs=%u", block_size, ctp -> block_size ) );
1153                 *re_init_cache = true;
1154             }
1155             else
1156             {
1157                 /* first we need to know how manny blocks the to-be-wrapped file needs */
1158                 uint64_t block_count  = SIZE_2_BLOCK_COUNT( content_size, block_size );
1159 
1160                 /* then we have to calculate how many bytes our bitmap will have */
1161                 uint64_t bitmap_bytes = BITS_2_BYTES( block_count );
1162 
1163                 /* new we can calculate the total size of our cache-file */
1164                 uint64_t calculated_cache_size = content_size +
1165                                     bitmap_bytes +
1166                                     sizeof( uint64_t ) +
1167                                     sizeof ( uint32_t );
1168 
1169                 if ( calculated_cache_size != ctp -> cache_size )
1170                 {
1171                     /* if the calculated and the actual cache-size do not match:
1172                        we should discard the cache file and start a new one! */
1173                     rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1174                     PLOGERR( klogErr, ( klogErr, rc, "calculated cache-size $(ls) does not match real cache-size $(rs)",
1175                                        "ls=%lu,rs=%lu", calculated_cache_size, ctp -> cache_size ) );
1176                     *re_init_cache = true;
1177                 }
1178                 else
1179                 {
1180                     atomic32_t * bitmap;
1181                     /* now make a bitmap-buffer using the size we calculated above */
1182                     rc = create_bitmap_buffer ( &bitmap, bitmap_bytes );
1183                     if ( rc != 0 )
1184                     {
1185                         LOGERR( klogErr, rc, "cannot create bitmap" );
1186                     }
1187                     else
1188                     {
1189                         /* now read the bitmap from the cache-file into our buffer */
1190                         size_t num_read;
1191                         rc_t rc = KFileReadAll ( ctp -> cache, content_size, ( void * ) bitmap, bitmap_bytes, &num_read );
1192                         if ( rc != 0 )
1193                         {
1194                             LOGERR( klogErr, rc, "cannot read bitmap from cache file" );
1195                             *re_init_cache = true;
1196                         }
1197                         else if ( num_read != bitmap_bytes )
1198                         {
1199                             rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1200                             PLOGERR( klogErr, ( klogErr, rc, "cannot read $(ls) bitmap-bytes from cache file, read $(rs) instead",
1201                                            "ls=%lu,rs=%lu", bitmap_bytes, num_read ));
1202                             *re_init_cache = true;
1203                         }
1204                         else
1205                         {
1206                             /* everything is verified now, finish it by AddRef to everything
1207                                we hold on */
1208                             rc = finish_tee( tee,
1209                                              ctp,
1210                                              block_count,
1211                                              bitmap_bytes,
1212                                              bitmap );
1213                         }
1214                         if ( rc != 0 )
1215                             free( ( void * ) bitmap );
1216                     }
1217                 }
1218             }
1219         }
1220     }
1221     return rc;
1222 }
1223 
initialize_new_cache_tee(struct KFile const ** tee,cache_tee_params * ctp)1224 static rc_t initialize_new_cache_tee ( struct KFile const **tee,
1225                                        cache_tee_params * ctp )
1226 {
1227     rc_t rc = 0;
1228 
1229     /* first we need to know how manny blocks the to-be-wrapped file needs */
1230     uint64_t block_count  = SIZE_2_BLOCK_COUNT( ctp -> to_wrap_size, ctp -> block_size );
1231 
1232     /* then we have to calculate how many bytes our bitmap will have */
1233     uint64_t bitmap_bytes = BITS_2_BYTES( block_count );
1234 
1235     /* new we can calculate the total size of our cache-file */
1236     ctp -> cache_size = ctp -> to_wrap_size +
1237                         bitmap_bytes +
1238                         sizeof( uint64_t ) +
1239                         sizeof ( uint32_t );
1240 
1241     /* now let us set the newly created cache - file to its required size */
1242     rc = KFileSetSize ( ctp -> cache, ctp -> cache_size );
1243     if ( rc != 0 )
1244     {
1245         LOGERR( klogErr, rc, "cannot set size of new cache-file" );
1246     }
1247     else
1248     {
1249         atomic32_t * bitmap;
1250         /* now make a bitmap-buffer using the size we calculated above */
1251         rc = create_bitmap_buffer ( &bitmap, bitmap_bytes );
1252         if ( rc != 0 )
1253         {
1254             LOGERR( klogErr, rc, "cannot create bitmap" );
1255         }
1256         else
1257         {
1258             /* write the bitmap and the tail into the cache-file */
1259             rc = write_bitmap_and_tail ( ctp -> cache,
1260                                          bitmap,
1261                                          ctp -> to_wrap_size,
1262                                          bitmap_bytes,
1263                                          ctp -> block_size );
1264             if ( rc == 0 )
1265             {
1266                 /* finish it by AddRef to everything we hold on
1267                    and */
1268                 rc = finish_tee( tee,
1269                                  ctp,
1270                                  block_count,
1271                                  bitmap_bytes,
1272                                  bitmap );
1273             }
1274             if ( rc != 0 )
1275                 free( ( void * ) bitmap );
1276         }
1277     }
1278     return rc;
1279 }
1280 
1281 /* called if no cache-file existed, or we had a broken one - and removed it */
create_new_cachetee(struct KFile const ** tee,cache_tee_params * ctp)1282 static rc_t create_new_cachetee( struct KFile const ** tee, cache_tee_params * ctp )
1283 {
1284     rc_t rc = KDirectoryCreateFile ( ctp -> dir,
1285                                      & ( ctp -> cache ),
1286                                      true,
1287                                      0664,
1288                                      kcmOpen | kcmParents,
1289                                      "%s.cache",
1290                                      ctp -> resolved_path );
1291     if ( rc != 0 )
1292     {
1293         LOGERR( klogErr, rc, "cannot create cache-file" );
1294     }
1295     else
1296     {
1297         /* we have the exclusive rd/wr access to the cache file !*/
1298         rc = initialize_new_cache_tee ( tee, ctp ); /* <========== */
1299         if ( rc != 0 )
1300         {
1301             KFileRelease ( ctp -> cache );
1302             /* try to remove the cache-file, something went wrong... */
1303             KDirectoryRemove ( ctp -> dir, true, "%s.cache", ctp -> resolved_path );
1304         }
1305     }
1306     return rc;
1307 }
1308 
KDirectoryVMakeCacheTee2(struct KDirectory * self,struct KFile const ** tee,struct KFile const * to_wrap,uint32_t block_size,const char * path,va_list args)1309 LIB_EXPORT rc_t CC KDirectoryVMakeCacheTee2 ( struct KDirectory * self,
1310                                              struct KFile const ** tee,
1311                                              struct KFile const * to_wrap,
1312                                              uint32_t block_size,
1313                                              const char * path,
1314                                              va_list args )
1315 {
1316     rc_t rc = 0;
1317 
1318     if ( tee == NULL )
1319         rc = RC ( rcFS, rcFile, rcAllocating, rcParam, rcNull );
1320     else
1321     {
1322         *tee = NULL;
1323         if ( to_wrap == NULL )
1324             rc = RC ( rcFS, rcFile, rcAllocating, rcParam, rcNull );
1325         else if ( self == NULL )
1326             rc = RC ( rcFS, rcFile, rcAllocating, rcSelf, rcNull );
1327         else if ( path == NULL )
1328             rc = RC ( rcFS, rcFile, rcAllocating, rcPath, rcNull );
1329         else if ( path [ 0 ] == 0 )
1330             rc = RC ( rcFS, rcFile, rcAllocating, rcPath, rcEmpty );
1331     }
1332 
1333     if ( rc == 0 )
1334     {
1335         cache_tee_params ctp;
1336         rc = KFileSize ( to_wrap, &( ctp . to_wrap_size ) );
1337         if ( rc != 0 )
1338         {
1339             LOGERR( klogErr, rc, "cannot detect size of file to be wrapped" );
1340         }
1341         else if ( ctp . to_wrap_size == 0 )
1342         {
1343             rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1344             LOGERR( klogErr, rc, "file to be wrapped is empty" );
1345         }
1346         else
1347         {
1348             rc = KDirectoryVResolvePath ( self,
1349                                           false, /* absolute */
1350                                           ctp . resolved_path,
1351                                           sizeof ctp . resolved_path,
1352                                           path,
1353                                           args );
1354             if ( rc != 0 )
1355             {
1356                 PLOGERR( klogErr, ( klogErr, rc, "cannot resolve path of cache file '$(path)'",
1357                         "path=%s", path ) );
1358             }
1359             else
1360             {
1361                 ctp . dir = self;
1362                 ctp . to_wrap = to_wrap;
1363                 ctp . block_size = ( block_size > 0 ) ? block_size : CACHE_TEE_DEFAULT_BLOCKSIZE;
1364                 ctp . resolved_path_size = string_size ( ctp . resolved_path );
1365                 ctp . read_only = false;
1366 
1367                 /* let the blocksize be a multiple of 16 */
1368                 ctp . block_size &= 0xFFFFF0;
1369                 if ( ctp . block_size < CACHE_TEE_MIN_BLOCKSIZE )
1370                 {
1371                     rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInsufficient );
1372                     LOGERR( klogErr, rc, "blocksize too small" );
1373                 }
1374                 else
1375                 {
1376                     /* lets see if we can open the cache-file in read/write - mode */
1377                     rc = KDirectoryOpenFileSharedWrite ( self,
1378                                                    &( ctp . cache ),
1379                                                    true,
1380                                                    "%s.cache",
1381                                                    ctp . resolved_path );
1382                     if ( rc == 0 )
1383                     {
1384                         bool re_init_cache = false;
1385                         /* we have the exclusive rd/wr access to the cache file !*/
1386                         rc = initialize_existing_cache_tee ( tee, &ctp, &re_init_cache );   /* <========== */
1387                         if ( rc != 0 )
1388                         {
1389                             KFileRelease ( ctp . cache );
1390                             if ( re_init_cache )
1391                             {
1392                                 /* let's try to get rid of the existing-cache-file! */
1393                                 rc = KDirectoryRemove ( self, false, "%s.cache", ctp . resolved_path );
1394                                 if ( rc == 0 )
1395                                 {
1396                                     /* we have removed the broken cache-file, let's try to create a new one */
1397                                     rc = create_new_cachetee ( tee, &ctp ); /* <========== */
1398                                 }
1399                             }
1400                         }
1401                     }
1402                     else if ( GetRCState( rc ) == rcNotFound )
1403                     {
1404                         /* cache-file does not exist, let's try to create it */
1405                         rc = create_new_cachetee ( tee, &ctp ); /* <========== */
1406                     }
1407                     else
1408                     {
1409                         /* let us try to open the cache in read-only mode !*/
1410                         rc = KDirectoryOpenFileRead ( self,
1411                                                       ( const struct KFile ** )&( ctp . cache ),
1412                                                       "%s.cache",
1413                                                       ctp . resolved_path );
1414                         if ( rc != 0 )
1415                         {
1416                             /* we cannot open the cache-file in read-only-mode */
1417                             LOGERR( klogErr, rc, "cannot open cache-file" );
1418                         }
1419                         else
1420                         {
1421                             bool re_init_cache = false;
1422                             ctp . read_only = true;
1423                             /* see if we can detect the size of the cache - file */
1424                             rc = initialize_existing_cache_tee ( tee, &ctp, &re_init_cache ); /* <========== */
1425                             if ( rc != 0 )
1426                             {
1427                                 KFileRelease ( ctp . cache );
1428                                 /* because we have only read-only-access,
1429                                    we cannot use the cache at all if re_init_cache is true! */
1430                             }
1431                         }
1432                     }
1433                 }
1434             }
1435         }
1436         if ( rc != 0 )
1437             rc = hand_out_to_wrap_file_as_tee_file ( tee, to_wrap );
1438 
1439     }
1440     return rc;
1441 }
1442 
1443 
KDirectoryMakeCacheTee2(struct KDirectory * self,struct KFile const ** tee,struct KFile const * to_wrap,uint32_t block_size,const char * path,...)1444 LIB_EXPORT rc_t CC KDirectoryMakeCacheTee2 ( struct KDirectory *self,
1445                                              struct KFile const **tee,
1446                                              struct KFile const *to_wrap,
1447                                              uint32_t block_size,
1448                                              const char *path, ... )
1449 {
1450     rc_t rc;
1451     va_list args;
1452     va_start ( args, path );
1453     rc = KDirectoryVMakeCacheTee2 ( self, tee, to_wrap, block_size, path, args );
1454     va_end ( args );
1455     return rc;
1456 }
1457 
1458 
count_bits_in_bitmap(const uint64_t block_count,const uint64_t bitmap_bytes,const atomic32_t * bitmap)1459 static uint64_t count_bits_in_bitmap( const uint64_t block_count, const uint64_t bitmap_bytes, const atomic32_t * bitmap )
1460 {
1461     uint64_t res = 0;
1462     uint64_t idx;
1463     for ( idx = 0; idx < block_count; ++idx )
1464     {
1465         if ( IS_BITMAP_BIT( bitmap, idx ) )
1466             res++;
1467     }
1468     return res;
1469 }
1470 
TruncateCacheTee2File(struct KFile * self)1471 LIB_EXPORT rc_t CC TruncateCacheTee2File( struct KFile * self )
1472 {
1473     rc_t rc;
1474     if ( self == NULL )
1475         rc = RC( rcFS, rcFile, rcResizing, rcSelf, rcNull );
1476     else
1477     {
1478         uint64_t file_size;
1479         rc = KFileSize( self, &file_size );
1480         if ( rc != 0 )
1481         {
1482             LOGERR( klogErr, rc, "cannot detect filesize when truncating cached file" );
1483         }
1484         else
1485         {
1486             uint32_t block_size;
1487             rc = read_block_size( self, file_size, &block_size );
1488             if ( rc == 0 )
1489             {
1490                 uint64_t content_size;
1491                 rc = read_content_size( self, file_size, &content_size );
1492                 if ( rc == 0 )
1493                 {
1494                     uint64_t block_count, bitmap_bytes;
1495                     rc = verify_file_structure( file_size, block_size, content_size, &block_count, &bitmap_bytes );
1496 
1497                     /* truncate the file by setting the new (shorter) filesize */
1498                     if ( rc == 0 )
1499                         rc = KFileSetSize( self, content_size );
1500                 }
1501             }
1502         }
1503     }
1504     return rc;
1505 }
1506 
GetCacheTee2FileCompleteness(const struct KFile * self,float * percent,uint64_t * bytes_in_cache)1507 LIB_EXPORT rc_t CC GetCacheTee2FileCompleteness( const struct KFile * self, float * percent, uint64_t * bytes_in_cache )
1508 {
1509     rc_t rc;
1510     if ( self == NULL )
1511         rc = RC( rcFS, rcFile, rcValidating, rcSelf, rcNull );
1512     else
1513     {
1514         uint64_t cache_size;
1515         if ( percent != NULL ) *percent = 0;
1516         if ( bytes_in_cache != NULL ) *bytes_in_cache = 0;
1517         rc = KFileSize( self, &cache_size );
1518         if ( rc != 0 )
1519         {
1520             LOGERR( klogErr, rc, "cannot detect filesize when checking if cached file is complete" );
1521         }
1522         else
1523         {
1524             uint32_t block_size;
1525             rc = read_block_size( self, cache_size, &block_size );
1526             if ( rc == 0 )
1527             {
1528                 uint64_t content_size;
1529                 rc = read_content_size( self, cache_size, &content_size );
1530                 /* create bitmap buffer */
1531                 if ( rc == 0 )
1532                 {
1533                     uint64_t block_count;
1534                     uint64_t bitmap_bytes;
1535                     rc = verify_file_structure( cache_size, block_size, content_size, &block_count, &bitmap_bytes );
1536                     if ( rc == 0 )
1537                     {
1538                         atomic32_t * bitmap = NULL;
1539                         rc = create_bitmap_buffer( &bitmap, bitmap_bytes );
1540                         if ( rc == 0 )
1541                         {
1542                             size_t num_read;
1543                             /* read the bitmap into the memory ... */
1544                             rc = KFileReadAll ( self, content_size, ( void * ) bitmap, bitmap_bytes, &num_read );
1545                             if ( rc != 0 )
1546                             {
1547                                 LOGERR( klogErr, rc, "cannot read bitmap from local file" );
1548                             }
1549                             else if ( num_read != bitmap_bytes )
1550                             {
1551                                 rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1552                                 PLOGERR( klogErr, ( klogErr, rc, "cannot read $(ls) bitmap-bytes from local file, read $(rs) instead",
1553                                                "ls=%lu,rs=%lu", bitmap_bytes, num_read ));
1554                             }
1555                         }
1556                         if ( rc == 0 )
1557                         {
1558                             uint64_t in_cache = count_bits_in_bitmap( block_count, bitmap_bytes, bitmap );
1559                             if ( in_cache > 0 && block_count > 0 )
1560                             {
1561                                 float res = ( float ) in_cache;
1562                                 res *= 100;
1563                                 res /= block_count;
1564                                 if ( percent != NULL ) ( *percent ) = res;
1565                                 if ( bytes_in_cache != NULL ) ( *bytes_in_cache ) = ( in_cache * block_size );
1566                             }
1567                         }
1568                         if ( bitmap != NULL )
1569                             free( ( void * ) bitmap );
1570                     }
1571                 }
1572             }
1573         }
1574     }
1575     return rc;
1576 }
1577 
1578 
1579 /* -----
1580  * examens the file, and reports the size of the original file ( without the cachefile-footer )
1581  *
1582  */
GetCacheTee2FileTruncatedSize(const struct KFile * self,uint64_t * truncated_size)1583 LIB_EXPORT rc_t CC GetCacheTee2FileTruncatedSize( const struct KFile * self, uint64_t * truncated_size )
1584 {
1585     rc_t rc;
1586     if ( self == NULL )
1587         rc = RC( rcFS, rcFile, rcValidating, rcSelf, rcNull );
1588     else if ( truncated_size == NULL )
1589         rc = RC( rcFS, rcFile, rcValidating, rcParam, rcNull );
1590     else
1591     {
1592         uint64_t cache_size;
1593         *truncated_size = 0;
1594         rc = KFileSize( self, &cache_size );
1595         if ( rc != 0 )
1596         {
1597             LOGERR( klogErr, rc, "cannot detect filesize when checking if cached file is complete" );
1598         }
1599         /* TODO: need to do a better constant tha a magic number */
1600         else if ( cache_size < 13 )
1601         {
1602             rc = RC ( rcFS, rcFile, rcValidating, rcSize, rcInsufficient );
1603             LOGERR( klogErr, rc, "cannot use filesize of zero when checking if cached file is complete" );
1604         }
1605         else
1606         {
1607             uint32_t block_size;
1608             rc = read_block_size( self, cache_size, &block_size );
1609             if ( rc == 0 )
1610             {
1611                 uint64_t content_size;
1612                 rc = read_content_size( self, cache_size, &content_size );
1613                 if ( rc == 0 )
1614                 {
1615                     uint64_t block_count, bitmap_bytes;
1616                     rc = verify_file_structure( cache_size, block_size, content_size, &block_count, &bitmap_bytes );
1617                     if ( rc == 0 )
1618                     {
1619                         *truncated_size = content_size;
1620                     }
1621                 }
1622             }
1623         }
1624     }
1625     return rc;
1626 }
1627 
IsThisCacheFileComplete(const struct KFile * self,bool * is_complete)1628 static rc_t CC IsThisCacheFileComplete( const struct KFile * self, bool * is_complete )
1629 {
1630     rc_t rc;
1631     if ( self == NULL )
1632         rc = RC( rcFS, rcFile, rcValidating, rcSelf, rcNull );
1633     else if ( is_complete == NULL )
1634         rc = RC( rcFS, rcFile, rcValidating, rcParam, rcNull );
1635     else
1636     {
1637         uint64_t file_size;
1638 
1639         *is_complete = false;
1640         rc = KFileSize( self, &file_size );
1641         if ( rc != 0 )
1642         {
1643             LOGERR( klogErr, rc, "cannot detect filesize when checking if cached file is complete" );
1644         }
1645         /* TODO: need to do a better constant tha a magic number */
1646         else if ( file_size < 13 )
1647         {
1648             rc = RC ( rcFS, rcFile, rcValidating, rcSize, rcInsufficient );
1649             LOGERR( klogErr, rc, "cannot use filesize of zero when checking if cached file is complete" );
1650         }
1651         else
1652         {
1653             uint32_t block_size;
1654             rc = read_block_size( self, file_size, &block_size );
1655             if ( rc == 0 )
1656             {
1657                 uint64_t content_size;
1658 
1659                 rc = read_content_size( self, file_size, &content_size );
1660                 /* create bitmap buffer */
1661                 if ( rc == 0 )
1662                 {
1663                     uint64_t block_count;
1664                     uint64_t bitmap_bytes;
1665                     rc = verify_file_structure( file_size, block_size, content_size, &block_count, &bitmap_bytes );
1666                     if ( rc == 0 )
1667                     {
1668                         atomic32_t * bitmap = NULL;
1669                         rc = create_bitmap_buffer( &bitmap, bitmap_bytes );
1670                         if ( rc == 0 )
1671                         {
1672                             size_t num_read;
1673                             /* read the bitmap into the memory ... */
1674                             rc = KFileReadAll ( self, content_size, ( void * ) bitmap, bitmap_bytes, &num_read );
1675                             if ( rc != 0 )
1676                             {
1677                                 LOGERR( klogErr, rc, "cannot read bitmap from local file" );
1678                             }
1679                             else if ( num_read != bitmap_bytes )
1680                             {
1681                                 rc = RC ( rcFS, rcFile, rcConstructing, rcParam, rcInvalid );
1682                                 PLOGERR( klogErr, ( klogErr, rc, "cannot read $(ls) bitmap-bytes from local file, read $(rs) instead",
1683                                                     "ls=%lu,rs=%lu", bitmap_bytes, num_read ));
1684                             }
1685                         }
1686                         if ( rc == 0 )
1687                             *is_complete = is_bitmap_full( bitmap, bitmap_bytes, block_count );
1688 
1689                         if ( bitmap != NULL )
1690                             free( ( void * ) bitmap );
1691                     }
1692                 }
1693             }
1694         }
1695     }
1696     return rc;
1697 }
1698 
1699 
1700 /* -----
1701  * reports in the boolean if the file is a cachetoofile and it is complete...
1702  *
1703  * KFile is the struct returned by KDirectoryMakeCacheTee()..
1704  */
IsCacheTee2FileComplete(const struct KFile * self,bool * is_complete)1705 LIB_EXPORT rc_t CC IsCacheTee2FileComplete( const struct KFile * self, bool * is_complete )
1706 {
1707     rc_t rc = 0;
1708     if ( self == NULL || is_complete == NULL )
1709         rc = RC ( rcFS, rcFile, rcValidating, rcParam, rcNull );
1710     else
1711     {
1712         if ( &self->vt->v1 != &vtKCacheTee2File_rw ||
1713              &self->vt->v1 != &vtKCacheTee2File_ro )
1714         {
1715             /* the given file is NOT a cache-tee-file */
1716             rc = IsThisCacheFileComplete( self, is_complete );
1717         }
1718         else
1719         {
1720             struct KCacheTee2File * ctf = ( struct KCacheTee2File * )self;
1721             *is_complete = is_bitmap_full( ctf->bitmap, ctf->bitmap_bytes, ctf->block_count );
1722         }
1723     }
1724     return rc;
1725 }
1726 
KFileIsKCacheTee2File(const struct KFile * self)1727 LIB_EXPORT bool CC KFileIsKCacheTee2File( const struct KFile * self )
1728 {
1729     bool res = false;
1730     if ( self != NULL )
1731     {
1732         res = &self->vt->v1 == &vtKCacheTee2File_rw;
1733         if ( !res ) res = &self->vt->v1 == &vtKCacheTee2File_ro;
1734     }
1735     return res;
1736 }
1737