1 /*
2  *  storage.cpp
3  *  swift
4  *
5  *  Created by Arno Bakker.
6  *  Copyright 2009-2016 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
7  *
8  * TODO:
9  * - Unicode?
10  * - Slow resume after alloc big file (Win32, work on swift-trunk)
11  */
12 
13 #include "swift.h"
14 #include "compat.h"
15 
16 #include <vector>
17 #include <utility>
18 
19 using namespace swift;
20 
21 
22 const std::string Storage::MULTIFILE_PATHNAME = "META-INF-multifilespec.txt";
23 const std::string Storage::MULTIFILE_PATHNAME_FILE_SEP = "/";
24 
25 #define DEBUGSTORAGE     0
26 
27 
Storage(std::string ospathname,std::string destdir,int td,uint64_t live_disc_wnd_bytes,std::string metamfspecospathname)28 Storage::Storage(std::string ospathname, std::string destdir, int td, uint64_t live_disc_wnd_bytes,
29                  std::string metamfspecospathname) :
30     Operational(),
31     state_(STOR_STATE_INIT),
32     os_pathname_(ospathname), destdir_(destdir), ht_(NULL), spec_size_(0),
33     single_fd_(-1), reserved_size_(-1), total_size_from_spec_(-1), last_sf_(NULL),
34     td_(td), alloc_cb_(NULL), live_disc_wnd_bytes_(live_disc_wnd_bytes), meta_mfspec_os_pathname_(metamfspecospathname)
35 {
36     // SIGNPEAK
37     if (live_disc_wnd_bytes > 0 && live_disc_wnd_bytes != POPT_LIVE_DISC_WND_ALL) {
38         state_ = STOR_STATE_SINGLE_LIVE_WRAP;
39         (void)OpenSingleFile();
40         return;
41     }
42 
43     std::string filename = os_pathname_;
44 
45     int64_t fsize = file_size_by_path_utf8(ospathname.c_str());
46     if (fsize < 0 && errno == ENOENT) {
47         // check if the metafile exists somewhere else
48         filename = meta_mfspec_os_pathname_;
49 
50         fsize = file_size_by_path_utf8(metamfspecospathname.c_str());
51         if (fsize < 0 && errno == ENOENT) {
52             // File does not exist, assume we're a client and all will be revealed
53             // (single file, multi-spec) when chunks come in.
54             return;
55         }
56     }
57 
58     // File exists. Check first bytes to see if a multifile-spec
59     FILE *fp = fopen_utf8(filename.c_str(),"rb");
60     if (!fp) {
61         dprintf("%s %s storage: File exists, but error opening\n", tintstr(), roothashhex().c_str());
62         print_error("Could not open existing storage file");
63         SetBroken();
64         return;
65     }
66 
67     char readbuf[1024];
68     int ret = fread(readbuf,sizeof(char),MULTIFILE_PATHNAME.length(),fp);
69     fclose(fp);
70     if (ret < 0) {
71         SetBroken();
72         return;
73     }
74 
75     if (!strncmp(readbuf,MULTIFILE_PATHNAME.c_str(),MULTIFILE_PATHNAME.length())) {
76         // Pathname points to a multi-file spec, assume we're seeding
77         // Arno, 2013-03-06: Not correct for a spec that doesn't fit in chunk 0,
78         // should attempt to parse spec, if good then _COMPLETE otherwise wait
79         // for chunks 1,2... and reparse.
80         //
81         state_ = STOR_STATE_MFSPEC_COMPLETE;
82 
83         dprintf("%s %s storage: Found multifile-spec, will seed it.\n", tintstr(), roothashhex().c_str());
84 
85         StorageFile *sf = new StorageFile(MULTIFILE_PATHNAME,0,fsize,filename);
86         if (!sf->IsOperational()) {
87             print_error("storage: multi-file spec file is not operational");
88             SetBroken();
89             return;
90         }
91         sfs_.push_back(sf);
92         if (ParseSpec(sf) < 0) {
93             print_error("storage: error parsing multi-file spec");
94             SetBroken();
95         }
96     } else {
97         // Normal swarm
98         dprintf("%s %s storage: Found single file, will check it.\n", tintstr(), roothashhex().c_str());
99 
100         state_ = STOR_STATE_SINGLE_FILE;
101         (void)OpenSingleFile();
102     }
103 }
104 
105 
~Storage()106 Storage::~Storage()
107 {
108     if (single_fd_ != -1)
109         close(single_fd_);
110 
111     storage_files_t::iterator iter;
112     for (iter = sfs_.begin(); iter < sfs_.end(); iter++) {
113         StorageFile *sf = *iter;
114         delete sf;
115     }
116     sfs_.clear();
117 }
118 
119 
Write(const void * buf,size_t nbyte,int64_t offset)120 ssize_t Storage::Write(const void *buf, size_t nbyte, int64_t offset)
121 {
122     if (DEBUGSTORAGE)
123         dprintf("%s %s storage: Write: fd %d nbyte " PRISIZET " off %" PRIi64 " state %" PRIi32 "\n", tintstr(),
124                 roothashhex().c_str(), single_fd_, nbyte,offset,state_);
125 
126     if (state_ == STOR_STATE_SINGLE_FILE) {
127         return pwrite(single_fd_, buf, nbyte, offset);
128     } else if (state_ == STOR_STATE_SINGLE_LIVE_WRAP) { // SIGNPEAK
129         int64_t newoff = offset % live_disc_wnd_bytes_;
130 
131         if (DEBUGSTORAGE)
132             dprintf("%s %d ?data writing disk %" PRIi64 " window %" PRIu64 "\n",tintstr(), 0, newoff, live_disc_wnd_bytes_);
133 
134         if (newoff+nbyte > live_disc_wnd_bytes_) {
135             // Writing more than window
136             size_t firstbyte = live_disc_wnd_bytes_ - newoff;
137             if (DEBUGSTORAGE)
138                 dprintf("%s %d ?data writing disk %" PRIi64 " firstbyte " PRISIZET "\n",tintstr(), 0, newoff, firstbyte);
139             int ret = pwrite(single_fd_, buf, firstbyte, newoff);
140             if (ret < 0)
141                 return ret;
142             else
143                 return Write(((char *)buf)+firstbyte,nbyte-firstbyte,offset+firstbyte);
144         } else
145             return pwrite(single_fd_, buf, nbyte, newoff);
146     }
147 
148     // MULTIFILE
149     if (state_ == STOR_STATE_INIT) {
150         if (offset != 0) {
151             dprintf("%s %s storage: Write: First write to offset >0, assume live\n", tintstr(), roothashhex().c_str());
152             //errno = EINVAL;
153             //return -1;
154         }
155 
156         if (DEBUGSTORAGE)
157             dprintf("%s %s storage: Write: chunk 0\n", tintstr(), roothashhex().c_str());
158 
159         // Check for multifile spec. If present, multifile, otherwise single
160         if (!strncmp((const char *)buf,MULTIFILE_PATHNAME.c_str(),strlen(MULTIFILE_PATHNAME.c_str()))) {
161             dprintf("%s %s storage: Write: Is multifile\n", tintstr(), roothashhex().c_str());
162 
163             // multifile entry will fit into first chunk
164             const char *bufstr = (const char *)buf;
165             int n = sscanf((const char *)&bufstr[strlen(MULTIFILE_PATHNAME.c_str())+1],"%" PRIi64 "",&spec_size_);
166             if (n != 1) {
167                 errno = EINVAL;
168                 return -1;
169             }
170 
171             //dprintf("%s %s storage: Write: multifile: specsize %" PRIi64 "\n", tintstr(), roothashhex().c_str(), spec_size_ );
172 
173             // Create StorageFile for multi-file spec.
174             StorageFile *sf = new StorageFile(MULTIFILE_PATHNAME,0,spec_size_,os_pathname_);
175             sfs_.push_back(sf);
176 
177             // Write all, or part of spec and set state_
178             return WriteSpecPart(sf,buf,nbyte,offset);
179         } else {
180             // Is a single file swarm.
181             state_ = STOR_STATE_SINGLE_FILE;
182 
183             int ret = OpenSingleFile();
184             if (ret < 0)
185                 return -1;
186 
187             // Write chunk to file via recursion.
188             return Write(buf,nbyte,offset);
189         }
190     } else if (state_ == STOR_STATE_MFSPEC_SIZE_KNOWN) {
191         StorageFile *sf = sfs_[0];
192 
193         dprintf("%s %s storage: Write: mf spec size known\n", tintstr(), roothashhex().c_str());
194 
195         return WriteSpecPart(sf,buf,nbyte,offset);
196     } else {
197         // state_ == STOR_STATE_MFSPEC_COMPLETE;
198         //dprintf("%s %s storage: Write: complete\n", tintstr(), roothashhex().c_str());
199 
200         StorageFile *sf = NULL;
201         if (last_sf_ != NULL && offset >= last_sf_->GetStart() && offset <= last_sf_->GetEnd())
202             sf = last_sf_;
203         else {
204             sf = FindStorageFile(offset);
205             if (sf == NULL) {
206                 dprintf("%s %s storage: Write: File not found!\n", tintstr(), roothashhex().c_str());
207                 errno = EINVAL;
208                 return -1;
209             }
210             last_sf_ = sf;
211         }
212 
213         std::pair<int64_t,int64_t> ht = WriteBuffer(sf,buf,nbyte,offset);
214         if (ht.first == -1) {
215             errno = EINVAL;
216             return -1;
217         }
218 
219         //dprintf("%s %s storage: Write: complete: first %" PRIi64 " second %" PRIi64 "\n", tintstr(), roothashhex().c_str(), ht.first, ht.second);
220 
221         if (ht.second > 0) {
222             // Write tail to next StorageFile(s) using recursion
223             const char *bufstr = (const char *)buf;
224             int ret = Write(&bufstr[ht.first], ht.second, offset+ht.first);
225             if (ret < 0)
226                 return ret;
227             else
228                 return ht.first+ret;
229         } else
230             return ht.first;
231     }
232 }
233 
234 
WriteSpecPart(StorageFile * sf,const void * buf,size_t nbyte,int64_t offset)235 int Storage::WriteSpecPart(StorageFile *sf, const void *buf, size_t nbyte, int64_t offset)
236 {
237     //dprintf("%s %s storage: WriteSpecPart: %s %d %" PRIi64 "\n", tintstr(), roothashhex().c_str(), sf->GetSpecPathName().c_str(), nbyte, offset );
238 
239     std::pair<int64_t,int64_t> ht = WriteBuffer(sf,buf,nbyte,offset);
240     if (ht.first == -1) {
241         errno = EINVAL;
242         return -1;
243     }
244 
245     if (offset+ht.first == sf->GetEnd()+1) {
246         // Wrote last part of spec
247         state_ = STOR_STATE_MFSPEC_COMPLETE;
248 
249         int ret = ParseSpec(sf);
250         if (ret < 0) {
251             errno = EINVAL;
252             return -1;
253         }
254 
255         // We know exact size after chunk 0, inform hash tree (which doesn't
256         // know until chunk N-1) is in.
257         ht_->set_size(GetSizeFromSpec());
258 
259         // Resize all files
260         ret = ResizeReserved(GetSizeFromSpec());
261         if (ret < 0)
262             return ret;
263 
264         // Write tail to next StorageFile(s) using recursion
265         const char *bufstr = (const char *)buf;
266         ret = Write(&bufstr[ht.first], ht.second, offset+ht.first);
267         if (ret < 0)
268             return ret;
269         else
270             return ht.first+ret;
271     } else {
272         state_ = STOR_STATE_MFSPEC_SIZE_KNOWN;
273         return ht.first;
274     }
275 }
276 
277 
278 
WriteBuffer(StorageFile * sf,const void * buf,size_t nbyte,int64_t offset)279 std::pair<int64_t,int64_t> Storage::WriteBuffer(StorageFile *sf, const void *buf, size_t nbyte, int64_t offset)
280 {
281     //dprintf("%s %s storage: WriteBuffer: %s %d %" PRIi64 "\n", tintstr(), roothashhex().c_str(), sf->GetSpecPathName().c_str(), nbyte, offset );
282 
283     int ret = -1;
284     if (offset+nbyte <= sf->GetEnd()+1) {
285         // Chunk belongs completely in sf
286         ret = sf->Write(buf,nbyte,offset - sf->GetStart());
287 
288         //dprintf("%s %s storage: WriteBuffer: Write: covered ret %d\n", tintstr(), roothashhex().c_str(), ret );
289 
290         if (ret < 0)
291             return std::make_pair(-1,-1);
292         else
293             return std::make_pair(nbyte,0);
294 
295     } else {
296         int64_t head = sf->GetEnd()+1 - offset;
297         int64_t tail = nbyte - head;
298 
299         // Write last part of file
300         ret = sf->Write(buf,head,offset - sf->GetStart());
301 
302         //dprintf("%s %s storage: WriteBuffer: Write: partial ret %d\n", tintstr(), roothashhex().c_str(), ret );
303 
304         if (ret < 0)
305             return std::make_pair(-1,-1);
306         else
307             return std::make_pair(head,tail);
308     }
309 }
310 
311 
312 
313 
FindStorageFile(int64_t offset)314 StorageFile * Storage::FindStorageFile(int64_t offset)
315 {
316     // Binary search for StorageFile that manages the given offset
317     int imin = 0, imax=sfs_.size()-1;
318     while (imax >= imin) {
319         int imid = (imin + imax) / 2;
320         if (offset >= sfs_[imid]->GetEnd()+1)
321             imin = imid + 1;
322         else if (offset < sfs_[imid]->GetStart())
323             imax = imid - 1;
324         else
325             return sfs_[imid];
326     }
327     // Should find it.
328     return NULL;
329 }
330 
331 
ParseSpec(StorageFile * sf)332 int Storage::ParseSpec(StorageFile *sf)
333 {
334     char *retstr = NULL,line[MULTIFILE_MAX_LINE+1];
335     FILE *fp = fopen_utf8(sf->GetOSPathName().c_str(),"rb");
336     if (fp == NULL) {
337         print_error("cannot open multifile-spec");
338         SetBroken();
339         return -1;
340     }
341 
342     int64_t offset=0;
343     int ret=0;
344     while (1) {
345         retstr = fgets(line,MULTIFILE_MAX_LINE,fp);
346         if (retstr == NULL)
347             break;
348 
349         // Format: "specpath filesize\n"
350         std::string pline(line);
351         size_t idx = pline.rfind(' ',pline.length()-1);
352 
353         std::string specpath = pline.substr(0,idx);
354         std::string sizestr = pline.substr(idx+1,pline.length());
355 
356         int64_t fsize=0;
357         int n = sscanf(sizestr.c_str(),"%" PRIi64 "",&fsize);
358         if (n == 0) {
359             ret = -1;
360             break;
361         }
362 
363         // Check pathname safety
364         if (specpath.substr(0,1) == MULTIFILE_PATHNAME_FILE_SEP) {
365             // Must not start with /
366             ret = -1;
367             break;
368         }
369         idx = specpath.find("..",0);
370         if (idx != std::string::npos) {
371             // Must not contain .. path escapes
372             ret = -1;
373             break;
374         }
375 
376         if (offset == 0) {
377             // sf already created for multifile-spec entry
378             offset += sf->GetSize();
379         } else {
380             // Convert specname to OS name
381             std::string ospath = destdir_+FILE_SEP;
382             ospath += Storage::spec2ospn(specpath);
383 
384             StorageFile *sf = new StorageFile(specpath,offset,fsize,ospath);
385             if (!sf->IsOperational()) {
386                 SetBroken();
387                 return -1;
388             }
389             sfs_.push_back(sf);
390             offset += fsize;
391         }
392     }
393 
394     // Assume: Multi-file spec sorted, so vector already sorted on offset
395     storage_files_t::iterator iter;
396     for (iter = sfs_.begin(); iter < sfs_.end(); iter++) {
397         StorageFile *sf = *iter;
398         dprintf("%s %s storage: parsespec: Got %s start %" PRIi64 " size %" PRIi64 "\n", tintstr(), roothashhex().c_str(),
399                 sf->GetSpecPathName().c_str(), sf->GetStart(), sf->GetSize());
400     }
401 
402     fclose(fp);
403     if (ret < 0) {
404         SetBroken();
405         return ret;
406     } else {
407         total_size_from_spec_ = offset;
408         return 0;
409     }
410 }
411 
412 
OpenSingleFile()413 int Storage::OpenSingleFile()
414 {
415     dprintf("%s %s storage: Opening single file %s\n", tintstr(), roothashhex().c_str(), os_pathname_.c_str());
416     single_fd_ = open_utf8(os_pathname_.c_str(),OPENFLAGS,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
417     if (single_fd_<0) {
418         single_fd_ = -1;
419         print_error("storage: cannot open single file");
420         SetBroken();
421         return -1;
422     }
423 
424     // Perform postponed resize.
425     if (reserved_size_ != -1) {
426         int ret = ResizeReserved(reserved_size_);
427         if (ret < 0) {
428             close(single_fd_);
429             single_fd_ = -1;
430             SetBroken();
431         }
432     }
433 
434     return single_fd_;
435 }
436 
437 
438 
439 
Read(void * buf,size_t nbyte,int64_t offset)440 ssize_t Storage::Read(void *buf, size_t nbyte, int64_t offset)
441 {
442     //dprintf("%s %s storage: Read: nbyte " PRISIZET " off %" PRIi64 "\n", tintstr(), roothashhex().c_str(), nbyte, offset );
443 
444     if (state_ == STOR_STATE_SINGLE_FILE) {
445         return pread(single_fd_, buf, nbyte, offset);
446     } else if (state_ == STOR_STATE_SINGLE_LIVE_WRAP) {
447         int64_t newoff = offset % live_disc_wnd_bytes_;
448         dprintf("%s %d ?data reading disk %" PRIi64 " window %" PRIu64 "\n",tintstr(), 0, newoff, live_disc_wnd_bytes_);
449 
450         return pread(single_fd_, buf, nbyte, newoff);
451     }
452 
453 
454     // MULTIFILE
455     if (state_ == STOR_STATE_INIT) {
456         errno = EINVAL;
457         return -1;
458     } else {
459         StorageFile *sf = NULL;
460         if (last_sf_ != NULL && offset >= last_sf_->GetStart() && offset <= last_sf_->GetEnd())
461             sf = last_sf_;
462         else {
463             sf = FindStorageFile(offset);
464             if (sf == NULL) {
465                 errno = EINVAL;
466                 return -1;
467             }
468             last_sf_ = sf;
469             //dprintf("%s %s storage: Read: Found file %s for off %" PRIi64 "\n", tintstr(), roothashhex().c_str(), sf->GetSpecPathName().c_str(), offset );
470         }
471 
472         ssize_t ret = sf->Read(buf,nbyte,offset - sf->GetStart());
473         if (ret < 0)
474             return ret;
475 
476         //dprintf("%s %s storage: Read: read %d\n", tintstr(), roothashhex().c_str(), ret );
477 
478         if (ret < nbyte && offset+ret != ht_->size()) {
479             //dprintf("%s %s storage: Read: want %d more\n", tintstr(), roothashhex().c_str(), nbyte-ret );
480 
481             // Not at end, and can fit more in buffer. Do recursion
482             char *bufstr = (char *)buf;
483             ssize_t newret = Read((void *)(bufstr+ret),nbyte-ret,offset+ret);
484             if (newret < 0)
485                 return newret;
486             else
487                 return ret + newret;
488         } else
489             return ret;
490     }
491 }
492 
493 
GetSizeFromSpec()494 int64_t Storage::GetSizeFromSpec()
495 {
496     if (state_ == STOR_STATE_SINGLE_FILE)
497         return -1;
498     else
499         return total_size_from_spec_;
500 }
501 
502 
503 
GetReservedSize()504 int64_t Storage::GetReservedSize()
505 {
506     if (state_ == STOR_STATE_SINGLE_FILE) {
507         return file_size(single_fd_);
508     } else if (state_ != STOR_STATE_MFSPEC_COMPLETE)
509         return -1;
510 
511     // MULTIFILE
512     storage_files_t::iterator iter;
513     int64_t totaldisksize=0;
514     for (iter = sfs_.begin(); iter < sfs_.end(); iter++) {
515         StorageFile *sf = *iter;
516 
517         dprintf("storage: getdisksize: statting %s\n", sf->GetOSPathName().c_str());
518 
519         int64_t fsize = file_size_by_path_utf8(sf->GetOSPathName().c_str());
520         if (fsize < 0) {
521             dprintf("%s %s storage: getdisksize: cannot stat file %s\n", tintstr(), roothashhex().c_str(),
522                     sf->GetOSPathName().c_str());
523             return fsize;
524         } else
525             totaldisksize += fsize;
526     }
527 
528     dprintf("storage: getdisksize: total already sized is %" PRIi64 "\n", totaldisksize);
529 
530     return totaldisksize;
531 }
532 
533 
GetMinimalReservedSize()534 int64_t Storage::GetMinimalReservedSize()
535 {
536     if (state_ == STOR_STATE_SINGLE_FILE) {
537         return 0;
538     } else if (state_ != STOR_STATE_MFSPEC_COMPLETE)
539         return -1;
540 
541     StorageFile *sf = sfs_[0];
542     return sf->GetSize();
543 }
544 
545 
ResizeReserved(int64_t size)546 int Storage::ResizeReserved(int64_t size)
547 {
548     // Arno, 2012-05-24: File allocation slow on Win32 without sparse files,
549     // make this detectable.
550     if (alloc_cb_ != NULL) {
551         alloc_cb_(td_,bin_t::NONE);
552         alloc_cb_ = NULL; // One time callback
553     }
554 
555     if (state_ == STOR_STATE_SINGLE_FILE) {
556         dprintf("%s %s storage: Resizing single file %d to %" PRIi64 "\n", tintstr(), roothashhex().c_str(), single_fd_, size);
557         return file_resize(single_fd_,size);
558     } else if (state_ == STOR_STATE_INIT) {
559         dprintf("%s %s storage: Postpone resize to %" PRIi64 "\n", tintstr(), roothashhex().c_str(), size);
560         reserved_size_ = size;
561         return 0;
562     } else if (state_ != STOR_STATE_MFSPEC_COMPLETE)
563         return -1;
564 
565     // MULTIFILE
566     if (size > GetReservedSize()) {
567         dprintf("%s %s storage: Resizing multi file to %" PRIi64 "\n", tintstr(), roothashhex().c_str(), size);
568 
569         // Resize files to wanted size, so pread() / pwrite() works for all offsets.
570         storage_files_t::iterator iter;
571         for (iter = sfs_.begin(); iter < sfs_.end(); iter++) {
572             StorageFile *sf = *iter;
573             int ret = sf->ResizeReserved();
574             if (ret < 0)
575                 return ret;
576         }
577     } else
578         dprintf("%s %s storage: Resize multi-file to <= %" PRIi64 ", ignored\n", tintstr(), roothashhex().c_str(), size);
579 
580     return 0;
581 }
582 
583 
spec2ospn(std::string specpn)584 std::string Storage::spec2ospn(std::string specpn)
585 {
586     std::string dest = specpn;
587     // compat.h I/O layer does UTF-8 to OS encoding
588     if (MULTIFILE_PATHNAME_FILE_SEP != FILE_SEP) {
589         // Replace OS filesep with spec
590         swift::stringreplace(dest,MULTIFILE_PATHNAME_FILE_SEP,FILE_SEP);
591     }
592     return dest;
593 }
594 
os2specpn(std::string ospn)595 std::string Storage::os2specpn(std::string ospn)
596 {
597     std::string dest = ospn;
598     // compat.h I/O layer does OS to UTF-8 encoding
599     if (MULTIFILE_PATHNAME_FILE_SEP != FILE_SEP) {
600         // Replace OS filesep with spec
601         swift::stringreplace(dest,FILE_SEP,MULTIFILE_PATHNAME_FILE_SEP);
602     }
603     return dest;
604 }
605 
606 
607 
608 /*
609  * StorageFile
610  */
611 
612 
613 
StorageFile(std::string specpath,int64_t start,int64_t size,std::string ospath)614 StorageFile::StorageFile(std::string specpath, int64_t start, int64_t size, std::string ospath) :
615     Operational(),
616     fd_(-1)
617 {
618     spec_pathname_ = specpath;
619     start_ = start;
620     end_ = start+size-1;
621     os_pathname_ = ospath;
622 
623     //fprintf(stderr,"StorageFile: os_pathname_ is %s\n", os_pathname_.c_str() );
624 
625     std::string normospath = os_pathname_;
626 #ifdef _WIN32
627     swift::stringreplace(normospath,"\\\\","\\");
628 #else
629     swift::stringreplace(normospath,"//","/");
630 #endif
631 
632     // Handle subdirs, if not multifilespec.txt
633     if (start_ != 0 && normospath.find(FILE_SEP,0) != std::string::npos) {
634         // Path contains dirs, make them
635         size_t i = 0;
636         while (true) {
637             i = normospath.find(FILE_SEP,i+1);
638             if (i == std::string::npos)
639                 break;
640             std::string path = normospath.substr(0,i);
641 #ifdef _WIN32
642             if (path.size() == 2 && path[1] == ':')
643                 // Windows drive spec, ignore
644                 continue;
645 #endif
646             int ret = file_exists_utf8(path.c_str());
647             if (ret <= 0) {
648                 ret = mkdir_utf8(path.c_str());
649 
650                 //fprintf(stderr,"StorageFile: mkdir %s returns %d\n", path.c_str(), ret );
651 
652                 if (ret < 0) {
653                     SetBroken();
654                     return;
655                 }
656             } else if (ret == 1) {
657                 // Something already exists and it is not a dir
658 
659                 dprintf("StorageFile: exists %s but is not dir %d\n", path.c_str(), ret);
660                 SetBroken();
661                 return;
662             }
663         }
664     }
665 
666 
667     // Open
668     fd_ = open_utf8(os_pathname_.c_str(),OPENFLAGS,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
669     if (fd_<0) {
670         //print_error("storage: file: Could not open");
671         dprintf("%s %s storage: file: Could not open %s\n", tintstr(), "0000000000000000000000000000000000000000",
672                 os_pathname_.c_str());
673         SetBroken();
674         return;
675     }
676 }
677 
~StorageFile()678 StorageFile::~StorageFile()
679 {
680     if (fd_>=0) {
681         close(fd_);
682     }
683 }
684 
685