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 #include <kfs/directory.h> /* KDirectoryNativeDir */
26
27 #include <klib/out.h> /* OUTMSG */
28 #include <klib/rc.h> /* RC */
29 #include <klib/status.h> /* STSMSG */
30 #include <klib/printf.h> /* string_printf */
31 #include <klib/time.h> /* KTimeStamp */
32
33 #include <strtol.h> /* strtou64 */
34
35 #include "PrfMain.h" /* RELEASE */
36 #include "PrfOutFile.h"
37
38 //#define DEBUGGING
39
StringRelease(const String * self)40 static rc_t StringRelease(const String *self) {
41 free((String*)self);
42
43 return 0;
44 }
45
KDirectory_MkTmpName(const KDirectory * self,const String * prefix,char * out,size_t sz)46 static rc_t KDirectory_MkTmpName(const KDirectory *self,
47 const String *prefix, char *out, size_t sz)
48 {
49 rc_t rc = string_printf(out, sz, NULL, "%S.tmp", prefix);
50 if (rc != 0) {
51 PLOGERR(
52 klogInt, (klogInt, rc, "string_printf($(arg))", "arg=%S", prefix));
53 return rc;
54 }
55 else
56 return rc;
57 }
58
KDirectory_Exist(const KDirectory * self,const String * name,const char * sfx)59 static bool KDirectory_Exist(const KDirectory * self,
60 const String * name, const char * sfx)
61 {
62 assert(name && sfx);
63
64 KPathType type = KDirectoryPathType(
65 self, "%.*s%s", name->size, name->addr, sfx);
66 if (type == kptNotFound)
67 return false;
68 else
69 return true;
70 }
71
72 #define EXT_1 ".pr"
73 #define EXT_BIN ".prf"
74 #define EXT_TXT ".prt"
75
TFExt(const PrfOutFile * self)76 static const char * TFExt(const PrfOutFile * self) {
77 switch (self->_tfType) {
78 case eTextual:
79 return EXT_TXT;
80 case eBinEol:
81 return ".prb";
82 case eBin8:
83 return EXT_BIN;
84 default:
85 assert(0);
86 return "";
87 }
88 }
89
TFExist(PrfOutFile * self)90 static bool TFExist(PrfOutFile * self) {
91 assert(self);
92
93 if (KDirectory_Exist(self->_dir, self->cache, TFExt(self)))
94 return true;
95 else
96 return false;
97 }
98
TFRm(PrfOutFile * self)99 static rc_t TFRm(PrfOutFile * self) {
100 assert(self);
101
102 if (TFExist(self)) {
103 assert(self->cache);
104 STSMSG(STS_DBG, ("removing %S%s", self->cache, TFExt(self)));
105 return KDirectoryRemove(self->_dir, false,
106 "%.*s%s", self->cache->size, self->cache->addr, TFExt(self));
107 }
108 else
109 return 0;
110 }
111
TFRmEmpty(PrfOutFile * self)112 static rc_t TFRmEmpty(PrfOutFile * self) {
113 if (TFExist(self)) {
114 const KFile * f = NULL;
115 uint64_t size = 0;
116 rc_t rc = KDirectoryOpenFileRead(self->_dir, &f,
117 "%.*s%s", self->cache->size, self->cache->addr, TFExt(self));
118 if (rc == 0)
119 rc = KFileSize(f, &size);
120 KFileRelease(f);
121 if (rc == 0 && size == 0)
122 return TFRm(self);
123 }
124
125 return 0;
126 }
127
TFKill(PrfOutFile * self,rc_t rc,const char * msg)128 static rc_t TFKill(PrfOutFile * self, rc_t rc, const char * msg) {
129 assert(self);
130
131 if (msg != NULL)
132 PLOGERR(klogInt, (klogInt, rc,
133 "Cannot keep transaction file: $(msg)", "msg=%s", msg));
134 else
135 LOGERR(klogInt, rc, "Cannot keep transaction file");
136
137 self->_resume = false;
138
139 return TFRm(self);
140 }
141
GetEnv(const char * name,uint64_t aDefault)142 static uint64_t GetEnv(const char *name, uint64_t aDefault) {
143 uint64_t val = 0;
144
145 const char * str = getenv(name);
146 if (str != NULL) {
147 char *end = NULL;
148 uint64_t n = strtou64(str, &end, 0);
149 if (end[0] != 0)
150 n = 0;
151 else
152 val = n;
153 }
154 else
155 val = 0;
156
157 if (val == 0)
158 return aDefault;
159 else
160 return val;
161 }
162
FTTimeToCommit(PrfOutFile * self)163 static bool FTTimeToCommit(PrfOutFile * self) {
164 static uint64_t D_PS = 0;
165 static KTime_t D_TM = 0;
166
167 if (D_PS == 0)
168 D_PS = GetEnv("NCBI_VDB_PREFETCH_COMMIT_SZ", ~0);
169 if (D_TM == 0)
170 D_TM = GetEnv("NCBI_VDB_PREFETCH_COMMIT_TM", 5 * 60);
171
172 assert(self);
173
174 if (self->_committed == 0) {
175 self->_committed = KTimeStamp();
176 return false;
177 }
178 else if (self->pos - self->_lastPos >= D_PS) {
179 STSMSG(STS_DBG, ("committing by PZ: pos=%ld", self->pos));
180 return true;
181 }
182 else if (D_PS != ~0) {
183 if (KTimeStamp() - self->_committed >= D_TM) {
184 STSMSG(STS_DBG, ("committing by TM: pos=%ld", self->pos));
185 return true;
186 }
187 else
188 return false;
189 }
190 else {
191 if (self->pos - self->_lastPos < 16 * 1024 * 1024)
192 return false;
193 else {
194 self->_lastPos = self->pos;
195 if (KTimeStamp() - self->_committed >= D_TM) {
196 STSMSG(STS_DBG, ("committing by TM: pos=%ld", self->pos));
197 return true;
198 }
199 else
200 return false;
201 }
202 }
203 }
204
FTToCommit(PrfOutFile * self)205 static void FTToCommit(PrfOutFile * self) {
206 assert(self);
207
208 self->_committed = KTimeStamp();
209 self->_lastPos = self->pos;
210 }
211
TFPutPosAsText(PrfOutFile * self,char * b,size_t sz,size_t * num)212 static rc_t TFPutPosAsText(PrfOutFile * self,
213 char * b, size_t sz, size_t * num)
214 {
215 rc_t rc = string_printf(b, sz, num, "%lu\n", self->pos);
216 if (rc != 0) {
217 PLOGERR(klogInt, (klogInt, rc, "string_printf($(arg).$(ext))",
218 "arg=%S,ext=%s", self->cache, TFExt(self)));
219 return rc;
220 }
221 else
222 return rc;
223 }
224
TFPutPosAsBinEol(PrfOutFile * self,char * b,size_t sz,size_t * num)225 static rc_t TFPutPosAsBinEol(PrfOutFile * self,
226 char * b, size_t sz, size_t * num)
227 {
228 assert(num);
229
230 assert(sz >= sizeof self->pos + 1);
231 memmove(b, &self->pos, sizeof self->pos);
232 *num = sizeof self->pos;
233
234 b[(*num)++] = '\n';
235
236 return 0;
237 }
238
239 #define MAGIC "NCBIprTr"
240
TFPutPosAsBin8(PrfOutFile * self,uint64_t tfPos,char * b,size_t sz,size_t * num)241 static rc_t TFPutPosAsBin8(PrfOutFile * self, uint64_t tfPos,
242 char * b, size_t sz, size_t * num)
243 {
244 int i = 0;
245
246 assert(num);
247
248 assert(sz >= sizeof self->pos + 8);
249
250 if (tfPos == 0) {
251 i = sizeof MAGIC - 1;
252 memmove(b, MAGIC, i);
253 }
254
255 memmove(b + i, &self->pos, sizeof self->pos);
256 *num = i + sizeof self->pos;
257
258 return 0;
259 }
260
261 static
TFPutPos(PrfOutFile * self,char * b,size_t sz,size_t * num)262 rc_t TFPutPos(PrfOutFile * self, char * b, size_t sz, size_t * num)
263 {
264 switch (self->_tfType) {
265 case eTextual:
266 return TFPutPosAsText(self, b, sz, num);
267 case eBinEol:
268 return TFPutPosAsBinEol(self, b, sz, num);
269 case eBin8:
270 return TFPutPosAsBin8(self, self->_tfPos, b, sz, num);
271 default:
272 assert(0);
273 return RC(rcExe, rcString, rcCreating, rcType, rcUnexpected);
274 }
275 }
276
TFWritePos(PrfOutFile * self)277 static rc_t TFWritePos(PrfOutFile * self) {
278 rc_t rc = 0;
279 char b[99] = "";
280 size_t num = 0;
281
282 assert(self && self->cache);
283
284 if (self->pos == 0)
285 return 0;
286
287 STSMSG(STS_DBG, ("writing %S%s", self->cache, TFExt(self)));
288 rc = TFPutPos(self, b, sizeof b, &num);
289 if (rc != 0) {
290 TFKill(self, rc, "Cannot write position");
291
292 return rc;
293 }
294 else {
295 size_t num_writ = 0;
296 rc = KFileWrite(self->_tf, self->_tfPos, b, num, &num_writ);
297 if (rc != 0)
298 TFKill(self, rc, "Cannot Write(prf)");
299 else
300 {
301 if (num_writ != num) {
302 rc = RC(rcExe, rcFile, rcWriting, rcTransfer, rcIncomplete);
303 TFKill(self, rc, "Cannot Write(prf)");
304 }
305 else
306 self->_tfPos += num_writ;
307 }
308
309 return rc;
310 }
311 }
312
TFSetPos(PrfOutFile * self,uint64_t pos,uint64_t tfPos)313 static rc_t TFSetPos(PrfOutFile * self, uint64_t pos, uint64_t tfPos) {
314 rc_t rc = 0;
315
316 assert(self);
317
318 self->pos = pos;
319 self->_tfPos = tfPos;
320
321 rc = KFileSetSize(self->_tf, self->_tfPos);
322 if (rc != 0) {
323 TFKill(self, rc, "Cannot SetSize(prf)");
324 return rc;
325 }
326 else
327 return rc;
328 }
329
TFGetPosAsText(PrfOutFile * self,uint64_t posSize,uint64_t fSize,uint64_t * pPos,uint64_t * pTfPos)330 static void TFGetPosAsText(PrfOutFile * self, uint64_t posSize, uint64_t fSize,
331 uint64_t *pPos, uint64_t *pTfPos)
332 {
333 const char * buf = NULL;
334 uint64_t first = 0, last = 0, prevLast = 0, prevPos = 0;
335
336 assert(self && pPos && pTfPos);
337
338 for (buf = self->_buf.base;;) {
339 uint64_t pos = 0;
340 size_t i = 0;
341 const char * p = NULL;
342
343 if (posSize < first) {
344 assert(posSize >= first);
345 first = posSize;
346 }
347
348 *pTfPos = first;
349
350 p = string_chr(buf + first, posSize - first, '\n');
351 if (p == NULL) {
352 *pPos = prevPos;
353 *pTfPos = prevLast + 1;
354 return;
355 }
356 else if (p == buf + first) {
357 *pPos = *pTfPos = 0;
358 return;
359 }
360
361 for (i = first, last = p - buf, pos = 0; i < last; ++i) {
362 if (buf[i] < '0' || buf[i] > '9') { /* non-digit: ignore bad file */
363 *pPos = *pTfPos = 0;
364 return;
365 }
366 else
367 pos = pos * 10 + buf[i] - '0';
368 }
369
370 if (pos < fSize) {
371 prevPos = pos;
372 prevLast = last;
373 first = last + 1;
374 }
375 else if (pos == fSize) {
376 *pPos = pos;
377 *pTfPos = last + 1;
378 return;
379 }
380 else {
381 *pPos = prevPos;
382 if (prevPos == 0)
383 *pTfPos = 0;
384 else
385 *pTfPos = prevLast + 1;
386 return;
387 }
388 }
389 }
390
TFGetPosAsBinEol(PrfOutFile * self,uint64_t posSize,uint64_t fSize,uint64_t * pPos,uint64_t * pTfPos)391 static void TFGetPosAsBinEol(PrfOutFile * self,
392 uint64_t posSize, uint64_t fSize, uint64_t *pPos, uint64_t *pTfPos)
393 {
394 uint64_t pos = 0, prevPos = 0;
395 uint64_t first = 0;
396 const char * buf = NULL;
397 assert(self && pPos && pTfPos);
398 for (buf = self->_buf.base, first = 0; first < posSize; ) {
399 if (first + sizeof pos + 1 > posSize) {
400 *pPos = *pTfPos = 0;
401 break;
402 }
403 memmove(&pos, buf + first, sizeof pos);
404 first += sizeof pos;
405 if (buf[first] != '\n') {
406 *pPos = *pTfPos = 0;
407 break;
408 }
409 ++first;
410 if (pos == fSize) {
411 *pPos = pos;
412 *pTfPos = first;
413 break;
414 }
415 else if (pos > fSize) {
416 *pPos = prevPos;
417 if (first < sizeof pos + 1)
418 *pTfPos = 0;
419 else
420 *pTfPos = first - sizeof pos - 1;
421 break;
422 }
423 else
424 prevPos = pos;
425 }
426 }
427
TFGetPosAsBin8(PrfOutFile * self,uint64_t posSize,uint64_t fSize,uint64_t * pPos,uint64_t * pTfPos)428 static rc_t TFGetPosAsBin8(PrfOutFile * self,
429 uint64_t posSize, uint64_t fSize, uint64_t *pPos, uint64_t *pTfPos)
430 {
431 uint64_t pos = 0, prevPos = 0;
432 uint64_t first = 0;
433 const char * buf = NULL;
434 bool found = false;
435
436 assert(self && pPos && pTfPos);
437
438 buf = self->_buf.base;
439 if (posSize < sizeof MAGIC - 1) {
440 *pPos = *pTfPos = 0;
441 return RC(rcExe, rcFile, rcReading, rcFile, rcInsufficient);
442 }
443
444 if (string_cmp(buf, sizeof MAGIC - 1, MAGIC, sizeof MAGIC - 1,
445 sizeof MAGIC - 1) != 0)
446 {
447 *pPos = *pTfPos = 0;
448 return RC(rcExe, rcFile, rcReading, rcData, rcInvalid);
449 }
450
451 buf += sizeof MAGIC - 1;
452 posSize -= sizeof MAGIC - 1;
453
454 for (first = 0; first < posSize; ) {
455 if (first + sizeof pos > posSize) {
456 *pPos = prevPos;
457 if (first < sizeof pos)
458 *pTfPos = 0;
459 else
460 *pTfPos = first - sizeof pos;
461 found = true;
462 break;
463 }
464
465 memmove(&pos, buf + first, sizeof pos);
466 first += sizeof pos;
467
468 if (pos == fSize) {
469 *pPos = pos;
470 *pTfPos = first;
471 found = true;
472 break;
473 }
474 else if (pos > fSize) {
475 *pPos = prevPos;
476 if (first < sizeof pos)
477 *pTfPos = 0;
478 else
479 *pTfPos = first - sizeof pos;
480 found = true;
481 break;
482 }
483 else
484 prevPos = pos;
485 }
486
487 if (!found) {
488 *pPos = prevPos;
489 *pTfPos = first;
490 }
491
492 *pTfPos += sizeof MAGIC - 1;
493
494 return 0;
495 }
496
TFGetPos(PrfOutFile * self,uint64_t posSize,uint64_t fSize,uint64_t * pPos,uint64_t * pTfPos)497 static rc_t TFGetPos(PrfOutFile * self, uint64_t posSize, uint64_t fSize,
498 uint64_t *pPos, uint64_t *pTfPos)
499 {
500 switch (self->_tfType) {
501 case eTextual:
502 TFGetPosAsText(self, posSize, fSize, pPos, pTfPos);
503 break;
504 case eBinEol:
505 TFGetPosAsBinEol(self, posSize, fSize, pPos, pTfPos);
506 break;
507 case eBin8:
508 return TFGetPosAsBin8(self, posSize, fSize, pPos, pTfPos);
509 default:
510 assert(0);
511 break;
512 }
513
514 return 0;
515 }
516
TFReadPos(PrfOutFile * self,uint64_t origSize)517 static rc_t TFReadPos(PrfOutFile * self, uint64_t origSize) {
518 rc_t rc = 0;
519 uint64_t fsize = 0;
520 uint64_t pos = 0, tfPos = 0;
521
522 assert(self);
523
524 STSMSG(STS_DBG, ("reading %S%s", self->cache, TFExt(self)));
525
526 if (origSize == 0)
527 return TFSetPos(self, 0, 0);
528
529 rc = KFileSize(self->_tf, &fsize);
530 if (rc != 0) {
531 TFKill(self, rc, "Cannot Size(prf)");
532 return rc;
533 }
534 if (rc == 0 && fsize == 0) {
535 self->pos = self->_tfPos = 0;
536 return rc;
537 }
538
539 if (rc == 0) {
540 rc = KDataBufferResize(&self->_buf, fsize);
541 if (rc != 0)
542 LOGERR(klogInt, rc, "KDataBufferResize");
543 }
544
545 if (rc == 0) {
546 rc = KFileReadExactly(self->_tf, 0, self->_buf.base, fsize);
547 if (rc != 0) {
548 TFKill(self, rc, "Cannot Read(prf)");
549 return rc;
550 }
551 }
552
553 rc = TFGetPos(self, fsize, origSize, &pos, &tfPos);
554 if (rc != 0)
555 return rc;
556 else
557 return TFSetPos(self, pos, tfPos);
558 }
559
TFNegotiatePos(PrfOutFile * self)560 static rc_t TFNegotiatePos(PrfOutFile * self) {
561 rc_t rc = 0;
562 uint64_t fsize = 0;
563
564 assert(self);
565
566 #ifdef DEBUGGING
567 OUTMSG(("%s: %S.prt found: loading...\n", __FUNCTION__, self->cache));
568 #endif
569 STSMSG(STS_DBG, ("loading %S%s", self->cache, TFExt(self)));
570
571 rc = KFileSize(self->file, &fsize);
572 if (rc != 0) {
573 self->_fatal = true;
574 PLOGERR(klogInt,
575 (klogInt, rc, "Cannot Size($(arg))", "arg=%s", self->tmpName));
576 }
577 else {
578 rc = TFReadPos(self, fsize);
579 if (rc != 0) {
580 self->pos = 0;
581 KFileSetSize(self->file, 0);
582 }
583 if (!self->_resume)
584 return rc;
585 }
586
587 if (!self->_fatal) {
588 if (rc == 0 && self->pos > 0)
589 self->_loaded = true;
590
591 assert(self->pos <= fsize);
592 if (self->pos > fsize)
593 self->pos = fsize; /* should never happen */
594 else if (self->pos < fsize) {
595 rc = KFileSetSize(self->file, self->pos);
596 if (rc != 0) {
597 self->_fatal = true;
598 PLOGERR(klogInt, (klogInt, rc,
599 "Cannot SetSize($(arg))", "arg=%s", self->tmpName));
600 }
601 }
602 }
603
604 if(self->_fatal)
605 STSMSG(STS_DBG, ("failed to load %S%s",self->cache, TFExt(self)));
606 else
607 STSMSG(STS_DBG, ("loaded %S%s: fsize=%lu, starting from %lu",
608 self->cache, TFExt(self), fsize, self->pos));
609 #ifdef DEBUGGING
610 OUTMSG(("%s: fsize=%lu, starting from %lu\n", __FUNCTION__,
611 fsize, self->pos));
612 #endif
613
614 return rc;
615 }
616
TFOpen(PrfOutFile * self,bool rm)617 static rc_t TFOpen(PrfOutFile * self, bool rm) {
618 rc_t rc = 0;
619 bool exists = false;
620
621 assert(self && self->cache);
622
623 if (KDirectory_Exist(self->_dir, self->cache, "")) {
624 STSMSG(STS_DBG, ("removing %S", self->cache));
625 KDirectoryRemove(self->_dir, false,
626 "%.*s", self->cache->size, self->cache->addr);
627 }
628
629 if (rm)
630 rc = TFRm(self);
631 else if (!self->_resume)
632 rc = TFRm(self);
633 else if (TFExist(self))
634 exists = true;
635
636 if (exists) {
637 STSMSG(STS_DBG, ("opening %S%s", self->cache, TFExt(self)));
638 rc = KDirectoryOpenFileWrite(self->_dir, &self->_tf, true, "%.*s%s",
639 self->cache->size, self->cache->addr, TFExt(self));
640 if (rc != 0)
641 PLOGERR(klogInt, (klogInt, rc, "Cannot OpenFileWrite(($(arg).prf)",
642 "arg=%S", self->cache));
643 }
644 else if (self->_resume) {
645 #ifdef DEBUGGING
646 OUTMSG(("%s: %S%s not found: creating...\n",
647 __FUNCTION__, self->cache, TFExt(self)));
648 #endif
649 STSMSG(STS_DBG, ("creating %S%s", self->cache, TFExt(self)));
650
651 rc = KDirectoryCreateFile(self->_dir, &self->_tf,
652 false, 0664, kcmInit | kcmParents, "%.*s%s",
653 self->cache->size, self->cache->addr, TFExt(self));
654 if (rc != 0)
655 PLOGERR(klogInt, (klogInt, rc, "Cannot CreateFile(($(arg).prf)",
656 "arg=%S", self->cache));
657
658 self->pos = self->_tfPos = 0;
659
660 if (rc == 0)
661 rc = TFWritePos(self);
662 }
663
664 if (rc != 0)
665 TFKill(self, rc, NULL);
666
667 return rc;
668 }
669
PrfOutFileOpenWrite(PrfOutFile * self)670 static rc_t PrfOutFileOpenWrite(PrfOutFile * self) {
671 rc_t rc = 0;
672
673 assert(self);
674
675 STSMSG(STS_DBG, ("opening %s", self->tmpName));
676
677 rc = KDirectoryOpenFileWrite(self->_dir,
678 &self->file, true, "%s", self->tmpName);
679 if (rc != 0) {
680 self->_fatal = true;
681 PLOGERR(klogInt, (klogInt, rc,
682 "Cannot OpenFileWrite($(arg))", "arg=%s", self->tmpName));
683 return rc;
684 }
685 else
686 return rc;
687 }
688
PrfOutFileCommit(PrfOutFile * self,bool force)689 static rc_t PrfOutFileCommit(PrfOutFile * self, bool force) {
690 rc_t rc = 0;
691
692 if (!self->_resume)
693 return 0;
694
695 if (force || FTTimeToCommit(self)) {
696 uint64_t size = 0;
697 rc = KFileRelease(self->file);
698 if (rc != 0) {
699 self->_fatal = true;
700 PLOGERR(klogInt, (klogInt, rc,
701 "Cannot Release($(arg))", "arg=%s", self->tmpName));
702 }
703 else
704 rc = PrfOutFileOpenWrite(self);
705
706 if (rc == 0) {
707 rc = KFileSize(self->file, &size);
708 if (rc != 0) {
709 self->_fatal = true;
710 PLOGERR(klogInt, (klogInt, rc,
711 "Cannot Size($(arg))", "arg=%s", self->tmpName));
712 }
713 }
714
715 if (rc == 0 && size < self->pos)
716 self->pos = size;
717
718 if (rc == 0)
719 rc = TFWritePos(self);
720
721 if (rc == 0) {
722 rc = KFileRelease(self->_tf);
723 if (rc != 0) {
724 TFKill(self, rc, "Cannot Release(prf)");
725 return rc;
726 }
727 }
728
729 if (rc == 0)
730 rc = TFOpen(self, false);
731
732 if (rc == 0)
733 FTToCommit(self);
734 }
735
736 return rc;
737 }
738
739 static
ConvertBinToTxt(const uint8_t * in,uint64_t sz,KFile * out)740 rc_t ConvertBinToTxt(const uint8_t * in, uint64_t sz, KFile * out)
741 {
742 rc_t rc = 0;
743 uint64_t from = 0, to = 0;
744 size_t num_writ = 0;
745
746 if (sz < 8)
747 return KFileWrite(out, to, in, sz, &num_writ);
748
749 rc = KFileWrite(out, to, in, 8, &num_writ);
750 to += num_writ;
751
752 if (rc == 0) {
753 rc = KFileWrite(out, to, "\n", 1, &num_writ);
754 to += num_writ;
755 }
756
757 for (from = 8; rc == 0 && from < sz;) {
758 union {
759 uint64_t u;
760 uint8_t b[8];
761 } n;
762 char buf[128] = "";
763 int i = 0;
764
765 for (i = 0, n.u = 0; rc == 0 && i < 8 && from < sz; ++i, ++from)
766 n.b[i] = in[from];
767
768 rc = string_printf(buf, sizeof buf, &num_writ, "%lu\n", n.u);
769
770 if (rc == 0) {
771 rc = KFileWrite(out, to, buf, num_writ, &num_writ);
772 to += num_writ;
773 }
774 }
775
776 return rc;
777 }
ConvertTxtToBin(const char * in,uint64_t sz,KFile * out)778 static rc_t ConvertTxtToBin(const char * in, uint64_t sz, KFile * out) {
779 rc_t rc = 0;
780 uint64_t from = 0, to = 0;
781 size_t num_writ = 0;
782
783 if (sz < 9)
784 return KFileWrite(out, to, in, sz, &num_writ);
785
786 rc = KFileWrite(out, to, in + from, 8, &num_writ);
787 to += num_writ;
788
789 for (from = 9; rc == 0 && from < sz;) {
790 bool truncate = false;
791 int len = 0;
792 union {
793 uint64_t u;
794 uint8_t b[8];
795 } n;
796
797 if (in[from] == '0')
798 truncate = true;
799 for (n.u = 0, len = 0; rc == 0 && from < sz && in[from] != '\n';
800 ++from, ++len)
801 {
802 if (in[from] < '0' || in[from] > '9')
803 return RC(rcExe, rcFile, rcReading, rcData, rcInvalid);
804 n.u = n.u * 10 + in[from] - '0';
805 }
806 ++from;
807
808 if (rc == 0) {
809 if (truncate) {
810 int i = 0;
811 for (i = 0; i < len; ++i, ++to)
812 rc = KFileWrite(out, to, &n.b[i], 1, NULL);
813 }
814 else {
815 rc = KFileWrite(out, to, &n.u, sizeof n.u, &num_writ);
816 to += num_writ;
817 }
818 }
819 }
820
821 return rc;
822 }
823
824 static
Convert(KDirectory * dir,const String * from,bool fromBin)825 rc_t Convert(KDirectory * dir, const String * from, bool fromBin)
826 {
827 rc_t rc = 0, r2 = 0;
828 const KFile * in = NULL;
829 KFile * out = NULL;
830 uint64_t fsize = 0;
831
832 KDataBuffer buf;
833 const String * to = NULL;
834
835 memset(&buf, 0, sizeof buf);
836 buf.elem_bits = 8;
837 rc = StringCopy(&to, from);
838
839 if (rc == 0) {
840 assert(to);
841 if (fromBin)
842 ((char*)to->addr)[to->size - 1] = 't';
843 else
844 ((char*)to->addr)[to->size - 1] = 'f';
845 OUTMSG(("%S -> %S\n", from, to));
846 }
847
848 if (rc == 0)
849 rc = KDirectoryOpenFileRead(dir, &in, "%s", from->addr);
850
851 if (rc == 0 && KDirectory_Exist(dir, to, ""))
852 rc = KDirectoryRemove(dir, false, "%s", to->addr);
853
854 if (rc == 0)
855 rc = KDirectoryCreateFile(dir, &out, false,
856 0664, kcmInit | kcmParents, "%s", to->addr);
857
858 if (rc == 0)
859 rc = KFileSize(in, &fsize);
860
861 if (rc == 0)
862 rc = KDataBufferResize(&buf, fsize);
863
864 if (rc == 0)
865 rc = KFileReadExactly(in, 0, buf.base, fsize);
866
867 RELEASE(KFile, in);
868
869 if (rc == 0) {
870 if (fromBin)
871 rc = ConvertBinToTxt(buf.base, fsize, out);
872 else
873 rc = ConvertTxtToBin(buf.base, fsize, out);
874 }
875
876 RELEASE(KFile, out);
877 RELEASE(String, to);
878
879 r2 = KDataBufferWhack(&buf);
880 if (rc == 0 && r2 != 0)
881 rc = r2;
882
883 return rc;
884 }
885
PrfOutFileInit(PrfOutFile * self,bool resume,const char * name,bool vdbcache)886 rc_t PrfOutFileInit(PrfOutFile * self, bool resume,
887 const char * name, bool vdbcache)
888 {
889 rc_t rc = 0;
890
891 assert(self);
892
893 memset(self, 0, sizeof *self);
894
895 self->_buf.elem_bits = 8;
896 self->_tfType = eBin8;
897 self->_resume = resume;
898 self->_name = name; /* don't free ! */
899 self->_vdbcache = vdbcache;
900
901 rc = KDirectoryNativeDir(&self->_dir);
902 if (rc != 0) {
903 LOGERR(klogInt, rc, "KDirectoryNativeDir");
904 return rc;
905 }
906 else
907 return rc;
908 }
909
PrfOutFileMkName(PrfOutFile * self,const String * cache)910 rc_t PrfOutFileMkName(PrfOutFile * self, const String * cache) {
911 rc_t rc = 0;
912
913 assert(self);
914
915 rc = KDirectory_MkTmpName(self->_dir, cache,
916 self->tmpName, sizeof self->tmpName);
917 if (rc == 0) {
918 rc = StringCopy(&self->cache, cache);
919 return rc;
920 }
921 else
922 return rc;
923 }
924
PrfOutFileOpen(PrfOutFile * self,bool force)925 rc_t PrfOutFileOpen(PrfOutFile * self, bool force) {
926 rc_t rc = 0;
927 bool negotiated = false;
928
929 rc_t ro = TFOpen(self, force);
930 if (ro != 0)
931 TFKill(self, ro, "Cannot open TF");
932
933 assert(self && self->cache);
934
935 if (KDirectoryPathType(self->_dir, "%s", self->tmpName)
936 == kptNotFound)
937 {
938 #ifdef DEBUGGING
939 OUTMSG(("%s: %s not found: creating...\n",
940 __FUNCTION__, self->tmpName));
941 #endif
942 STSMSG(STS_DBG, ("%s not found: creating...", self->tmpName));
943
944 rc = KDirectoryCreateFile(self->_dir, &self->file,
945 false, 0664, kcmInit | kcmParents, "%s", self->tmpName);
946 if (rc != 0)
947 PLOGERR(klogInt, (klogInt, ro, "Cannot CreateFile($(arg))",
948 "arg=%s", self->tmpName));
949 }
950 else {
951 rc = PrfOutFileOpenWrite(self);
952 if (rc == 0) {
953 if (force || !self->_resume) {
954 self->pos = 0;
955 if (force)
956 STSMSG(STS_DBG, ("forced to ignore transaction file"));
957 else
958 STSMSG(STS_DBG, (
959 "ignoring transaction file by command line option"));
960 }
961 else if (ro == 0) {
962 ro = TFNegotiatePos(self);
963 if (ro == 0)
964 negotiated = true;
965 else if (self->_fatal && rc == 0)
966 rc = ro;
967 }
968 }
969 }
970
971 if (rc == 0 && ro == 0 && !negotiated) {
972 self->pos = self->_tfPos = 0;
973
974 if (self->_resume) {
975 ro = KFileSetSize(self->_tf, 0);
976 if (ro != 0)
977 TFKill(self, ro, "Cannot SetSize(prf)");
978 }
979 }
980
981 if (rc == 0) {
982 uint64_t fsize = 0;
983 rc = KFileSize(self->file, &fsize);
984 DISP_RC2(rc, "Cannot Size", self->tmpName);
985 if (rc == 0) {
986 if (self->pos < fsize) {
987 rc = KFileSetSize(self->file, self->pos);
988 DISP_RC2(rc, "Cannot SetSize", self->tmpName);
989 }
990 else if (self->pos > fsize)
991 self->pos = fsize;
992 }
993 }
994
995 if (rc == 0 && self->pos > 0)
996 STSMSG(STS_TOP, (" Continue download of '%s%s' from %lu",
997 self->_name, self->_vdbcache ? ".vdbcache" : "", self->pos));
998
999 #ifdef DEBUGGING
1000 OUTMSG(("%s: start from %lu\n", __FUNCTION__, self->pos));
1001 #endif
1002
1003 return rc;
1004 }
1005
PrfOutFileIsLoaded(const PrfOutFile * self)1006 bool PrfOutFileIsLoaded(const PrfOutFile * self) {
1007 assert(self);
1008
1009 if (self->_loaded)
1010 return true;
1011 else
1012 return false;
1013 }
1014
PrfOutFileCommitTry(PrfOutFile * self)1015 rc_t PrfOutFileCommitTry(PrfOutFile * self) {
1016 return PrfOutFileCommit(self, false);
1017 }
1018
PrfOutFileCommitDo(PrfOutFile * self)1019 rc_t PrfOutFileCommitDo(PrfOutFile * self) {
1020 if (self->_resume) {
1021 STSMSG(STS_DBG, ("committing on exit: pos=%ld", self->pos));
1022 return PrfOutFileCommit(self, true);
1023 }
1024 else
1025 return 0;
1026 }
1027
PrfOutFileClose(PrfOutFile * self)1028 rc_t PrfOutFileClose(PrfOutFile * self) {
1029 rc_t rc = 0, r2 = 0;
1030
1031 assert(self);
1032
1033 KFileRelease(self->_tf);
1034 self->_tf = NULL;
1035
1036 RELEASE(KFile, self->file);
1037
1038 r2 = KDataBufferWhack(&self->_buf);
1039 if (rc == 0 && r2 != 0)
1040 rc = r2;
1041
1042 return rc;
1043 }
1044
PrfOutFileWhack(PrfOutFile * self,bool success)1045 rc_t PrfOutFileWhack(PrfOutFile * self, bool success) {
1046 rc_t rc = 0;
1047
1048 #ifndef DEBUGGINGG
1049 if (success && !self->invalid)
1050 rc = TFRm(self);
1051 else if (!success && !self->invalid)
1052 rc = TFRmEmpty(self);
1053 #endif
1054
1055 RELEASE(String, self->cache);
1056 RELEASE(KDirectory, self->_dir);
1057
1058 return rc;
1059 }
1060
PrfOutFileConvert(KDirectory * dir,const char * path,bool * recognized)1061 rc_t PrfOutFileConvert(KDirectory * dir, const char * path,
1062 bool * recognized)
1063 {
1064 assert(recognized);
1065 *recognized = false;
1066
1067 if (path == NULL)
1068 return 0;
1069
1070 else {
1071 String s;
1072 StringInitCString(&s, path);
1073 if (s.size < sizeof EXT_BIN)
1074 return 0;
1075
1076 if (string_cmp(s.addr + s.size - sizeof EXT_1, sizeof EXT_1 - 1,
1077 EXT_1, sizeof EXT_1 - 1, sizeof EXT_1 - 1) != 0)
1078 {
1079 return 0;
1080 }
1081
1082 switch (s.addr[s.size - 1]) {
1083 case 'f':
1084 *recognized = true;
1085 return Convert(dir, &s, true);
1086 case 't':
1087 *recognized = true;
1088 return Convert(dir, &s, false);
1089 default:
1090 return 0;
1091 }
1092 }
1093 }
1094
1095 /******************************************************************************/
1096