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