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