1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application, but
5  * could be used for other applications which could use the PoolFile
6  * class's functionality.
7  *
8  * PoolFile is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published
10  * by the Free Software Foundation; either version 2 of the License,
11  * or (at your option) any later version.
12  *
13  * PoolFile is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
21  */
22 
23 #include "CMultiFile.h"
24 
25 #include <limits.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdio.h> // for rename
29 
30 #include <stdexcept>
31 #include <algorithm>
32 
33 #include <istring>
34 
35 #include <CPath.h>
36 #include <endian_util.h>
37 
38 //#if defined(__SOLARIS_GNU_CPP__) || defined(__SOLARIS_SUN_CPP__) || defined(__LINUX_GNU_CPP__)
39 	// ??? if windows is posix... I think it should have these necessary files... We'll see when we try to compile there
40 	#include <sys/types.h>
41 	#include <sys/stat.h>
42 	#include <fcntl.h>
43 	#include <unistd.h>
44 
45 /*
46 #elif defined(WIN32)
47 	#include <sys\stat.h>
48 	#include <share.h>
49 	#include <io.h>
50 	#include <stddef.h>
51 	#include <dos.h>
52 
53 #else
54 	#error implementation not handled
55 #endif
56 */
57 
58 #define HEADER_SIZE 512
59 
60 #define PHYSICAL_MAX_FILE_SIZE ((CMultiFile::l_addr_t)2147000000) // ??? this needs to change based on off_t's type... won't be a problem when simple 64bit fs is in place commonly
61 #define LOGICAL_MAX_FILE_SIZE ((CMultiFile::l_addr_t)(PHYSICAL_MAX_FILE_SIZE-HEADER_SIZE))
62 
63 #define CMULTIFILE_SIGNATURE (*((uint32_t *)"CMFL"))
64 
65 
66 
67 /* -- CMultiFile --
68  *
69  * - This is the file I/O class which (mainly) TPoolFile uses to read and write to disk
70  *   	- When 64 bit file systems are the norm, I should be able to reimplement this
71  *   	  class for TPoolFile that just calls the normal file system functions
72  *
73  * - This class maps a larger address space onto multiple files
74  *
75  * - When 64 bit file systems become the norm this class should really be obsolete (for a
76  *   time).
77  *
78  * - One practical consideration is that to provide full 64bit address access by using
79  *   multiple files, it would require up to 8 billion 31bit files to actually access a 64
80  *   bit sized space.   My purpose was not necessarily to provide 18Tb of space to
81  *   TPoolFile, but to merely provide much more than 2Gb.  So I have imposed a limit on the
82  *   number of files, MAX_OPEN_FILES, because it has to keep all the files opened.  Perhaps
83  *   I avoid keeping them all open at once, but again my purpose was just to provide more
84  *   than 2Gb of storage.   When 64bit file systems are the normal in a few years, this
85  *   won't even be an issue.
86  *
87  * - This class works by taking the address space and simply assigning the first 2 billion
88  *   addresses to the first file, then the next 2 billion addresses to the second file and
89  *   so on.  However, there is a 512 byte header at the beginning of each file which is used
90  *   to tie the files in a set together.
91  *
92  * - endianness is handled by always writing the header as the native endian except that the
93  *   signature is +1 if written as big endian so that when the file is opened again, it is
94  *   known whether endian swapping is necessary.  Also, the signature is always written
95  *   little endian
96  *
97  */
98 
99 // TODO need to check return values of lseek
100 
CMultiFile()101 CMultiFile::CMultiFile() :
102 	opened(false),
103 	initialFilename(""),
104 	openFileCount(0)
105 {
106 	for(size_t t=0;t<MAX_OPEN_FILES;t++)
107 		openFiles[t]=-1;
108 }
109 
~CMultiFile()110 CMultiFile::~CMultiFile()
111 {
112 	close(false);
113 }
114 
open(const string _initialFilename,const bool canCreate)115 void CMultiFile::open(const string _initialFilename,const bool canCreate)
116 {
117 	if(opened)
118 		throw runtime_error(string(__func__)+" -- already opened");
119 
120 	initialFilename=_initialFilename;
121 	openFileCount=0;
122 
123 	try
124 	{
125 		if(canCreate)
126 			CPath(initialFilename).touch();
127 
128 		// open first file
129 		RFileHeader header;
130 		openFile(initialFilename,header,true);
131 		matchSignature=header.matchSignature;
132 
133 		// attempt to open all the other files
134 		for(uint64_t t=1;t<header.fileCount;t++)
135 			openFile(buildFilename(t),header);
136 
137 		writeHeaderToFiles();
138 
139 		opened=true;
140 
141 		totalSize=calcTotalSize();
142 	}
143 	catch(...)
144 	{
145 		close();
146 		throw;
147 	}
148 }
149 
close(bool removeFiles)150 void CMultiFile::close(bool removeFiles)
151 {
152 	for(size_t t=0;t<MAX_OPEN_FILES;t++)
153 	{
154 		if(openFiles[t]>0)
155 		{
156 			::close(openFiles[t]);
157 			openFiles[t]=-1;
158 
159 			if(removeFiles)
160 				unlink(buildFilename(t).c_str());
161 		}
162 	}
163 	openFileCount=0;
164 
165 	initialFilename="";
166 	totalSize=0;
167 	opened=false;
168 }
169 
rename(const string newInitialFilename)170 void CMultiFile::rename(const string newInitialFilename)
171 {
172 	const size_t origOpenFileCount=openFileCount;
173 	const string origInitialFilename=initialFilename;
174 	close(false);
175 
176 	for(size_t t=0;t<origOpenFileCount;t++)
177 		::rename(buildFilename(t,origInitialFilename).c_str(),buildFilename(t,newInitialFilename).c_str());
178 
179 	open(newInitialFilename,false);
180 }
181 
seek(const l_addr_t _position,RHandle & handle)182 void CMultiFile::seek(const l_addr_t _position,RHandle &handle)
183 {
184 	if(_position>totalSize)
185 		throw runtime_error(string(__func__)+" -- attempting to seek beyond the end of the file size: "+istring(_position));
186 	handle.position=_position;
187 }
188 
tell(RHandle & handle) const189 const CMultiFile::l_addr_t CMultiFile::tell(RHandle &handle) const
190 {
191 	return handle.position;
192 }
193 
read(void * buffer,const l_addr_t count,RHandle & handle)194 void CMultiFile::read(void *buffer,const l_addr_t count,RHandle &handle)
195 {
196 	if(!opened)
197 		throw runtime_error(string(__func__)+" -- not opened");
198 	if((totalSize-handle.position)<count)
199 		throw runtime_error(string(__func__)+" -- attempting to read beyond the end of the file size; position: "+istring(handle.position)+" count: "+istring(count));
200 
201 	size_t whichFile=handle.position/LOGICAL_MAX_FILE_SIZE;
202 	f_addr_t whereFile=handle.position%LOGICAL_MAX_FILE_SIZE;
203 
204 	l_addr_t lengthToRead=count;
205 	while(lengthToRead>0)
206 	{
207 
208 		const f_addr_t seekRet=lseek(openFiles[whichFile],whereFile+HEADER_SIZE,SEEK_SET);
209 		if(seekRet==((f_addr_t)-1))
210 		{
211 			int errNO=errno;
212 			throw runtime_error(string(__func__)+" -- error seeking in file: "+buildFilename(whichFile)+" -- strerror: "+strerror(errNO));
213 		}
214 
215 		const size_t stripRead=min(lengthToRead,LOGICAL_MAX_FILE_SIZE-whereFile);
216 		const ssize_t lengthRead=::read(openFiles[whichFile],(uint8_t *)buffer+(count-lengthToRead),stripRead);
217 		if(lengthRead<0)
218 		{
219 			int errNO=errno;
220 			throw runtime_error(string(__func__)+" -- error reading from file: "+buildFilename(whichFile)+" -- where: "+istring(whereFile)+"+"+istring(HEADER_SIZE)+" lengthRead/stripRead: "+istring(lengthRead)+"/"+istring(stripRead)+" strerror: "+strerror(errNO));
221 		}
222 		else if((size_t)lengthRead!=stripRead)
223 		{
224 			throw runtime_error(string(__func__)+" -- error reading from file: "+buildFilename(whichFile)+" -- where: "+istring(whereFile)+"+"+istring(HEADER_SIZE)+" lengthRead/stripRead: "+istring(lengthRead)+"/"+istring(stripRead));
225 		}
226 
227 		lengthToRead-=stripRead;
228 		handle.position+=stripRead;
229 
230 		whichFile++;	// read from the next file next go around
231 		whereFile=0;	// each additional file to read from should start at 0 now
232 	}
233 }
234 
read(void * buffer,const l_addr_t count,const l_addr_t _position)235 void CMultiFile::read(void *buffer,const l_addr_t count,const l_addr_t _position)
236 {
237 	RHandle handle;
238 	seek(_position,handle);
239 	read(buffer,count,handle);
240 }
241 
write(const void * buffer,const l_addr_t count,RHandle & handle)242 void CMultiFile::write(const void *buffer,const l_addr_t count,RHandle &handle)
243 {
244 	if(!opened)
245 		throw runtime_error(string(__func__)+" -- not opened");
246 	if((getAvailableSize()-handle.position)<count)
247 		throw runtime_error(string(__func__)+" -- insufficient space to write "+istring(count)+" more bytes");
248 
249 	// grow file(s) if necessary
250 	if(totalSize<(handle.position+count))
251 		prvSetSize(handle.position+count,count);
252 
253 	size_t whichFile=handle.position/LOGICAL_MAX_FILE_SIZE;
254 	f_addr_t whereFile=(handle.position%LOGICAL_MAX_FILE_SIZE);
255 
256 	l_addr_t lengthToWrite=count;
257 	while(lengthToWrite>0)
258 	{
259 
260 		const f_addr_t seekRet=lseek(openFiles[whichFile],whereFile+HEADER_SIZE,SEEK_SET);
261 		if(seekRet==((f_addr_t)-1))
262 		{
263 			int errNO=errno;
264 			throw runtime_error(string(__func__)+" -- error seeking in file: "+buildFilename(whichFile)+" -- where: "+istring(whereFile)+"+"+istring(HEADER_SIZE)+" strerror: "+strerror(errNO));
265 		}
266 
267 		const size_t stripWrite=min(lengthToWrite,LOGICAL_MAX_FILE_SIZE-whereFile);
268 		const ssize_t lengthWritten=::write(openFiles[whichFile],(uint8_t *)buffer+(count-lengthToWrite),stripWrite);
269 		if(lengthWritten<0)
270 		{
271 			int errNO=errno;
272 			throw runtime_error(string(__func__)+" -- error writing to file: "+buildFilename(whichFile)+" -- where: "+istring(whereFile)+"+"+istring(HEADER_SIZE)+" lengthWritten/stripWrite: "+istring(lengthWritten)+"/"+istring(stripWrite)+" strerror: "+strerror(errNO));
273 		}
274 		else if((size_t)lengthWritten!=stripWrite)
275 		{
276 			throw runtime_error(string(__func__)+" -- error writing to file: "+buildFilename(whichFile)+" -- where: "+istring(whereFile)+"+"+istring(HEADER_SIZE)+" lengthWritten/stripWrite: "+istring(lengthWritten)+"/"+istring(stripWrite)+" -- perhaps the disk is full");
277 		}
278 
279 		lengthToWrite-=lengthWritten;
280 		handle.position+=lengthWritten;
281 
282 		whichFile++;	// write to the next file next go around
283 		whereFile=0;	// each additional file to write to should start at 0 now
284 	}
285 }
286 
write(const void * buffer,const l_addr_t count,const l_addr_t _position)287 void CMultiFile::write(const void *buffer,const l_addr_t count,const l_addr_t _position)
288 {
289 	RHandle handle;
290 	seek(_position,handle);
291 	write(buffer,count,handle);
292 }
293 
setSize(const l_addr_t newSize)294 void CMultiFile::setSize(const l_addr_t newSize)
295 {
296 	prvSetSize(newSize,0);
297 }
298 
prvSetSize(const l_addr_t newSize,const l_addr_t forWriteSize)299 void CMultiFile::prvSetSize(const l_addr_t newSize,const l_addr_t forWriteSize)
300 {
301 	if(newSize>totalSize && getAvailableSize()<newSize)
302 		throw runtime_error(string(__func__)+" -- insufficient space to grow data size to: "+istring(newSize));
303 
304 	size_t neededFileCount=(newSize/LOGICAL_MAX_FILE_SIZE)+1;
305 	const f_addr_t currentLastFilesSize=(totalSize%LOGICAL_MAX_FILE_SIZE)+HEADER_SIZE;
306 	const f_addr_t lastFilesSize=(newSize%LOGICAL_MAX_FILE_SIZE)+HEADER_SIZE;
307 
308 	if(neededFileCount>openFileCount)
309 	{ // create some new files
310 		if(neededFileCount>MAX_OPEN_FILES)
311 			throw runtime_error(string(__func__)+" -- would have to open too many files ("+istring(neededFileCount)+") to set size to "+istring(newSize));
312 
313 		// set the last file now its max size
314 		setFileSize(openFiles[openFileCount-1],PHYSICAL_MAX_FILE_SIZE);
315 
316 		// create new files and set all but the last to their max size
317 		for(size_t t=openFileCount;t<neededFileCount;t++)
318 		{
319 			const string filename=buildFilename(t);
320 			CPath(filename).touch();
321 			openFile(filename,*((RFileHeader *)NULL),false,false);
322 			if(t!=neededFileCount-1)
323 				setFileSize(openFiles[openFileCount-1],PHYSICAL_MAX_FILE_SIZE);
324 		}
325 
326 		writeHeaderToFiles(); // write the new file count to all the files (could probably get by with just writing it to the first one???)
327 	}
328 	else
329 	{
330 		// close and remove some files
331 		for(size_t t=neededFileCount;t<openFileCount;t++)
332 		{
333 			::close(openFiles[t]);
334 			openFiles[t]=-1;
335 			unlink(buildFilename(t).c_str());
336 		}
337 		openFileCount=neededFileCount;
338 
339 		writeHeaderToFiles(); // write the new file count to all the files (could probably get by with just writing it to the first one???)
340 	}
341 
342 
343 
344 	// set size of last file
345 	if(newSize<totalSize || ((l_addr_t)lastFilesSize-forWriteSize)>(l_addr_t)currentLastFilesSize)
346 		setFileSize(openFiles[openFileCount-1],lastFilesSize-forWriteSize);
347 	totalSize=newSize;
348 }
349 
sync() const350 void CMultiFile::sync() const
351 {
352 	/*
353 	for(size_t t=0;t<openFileCount;t++)
354 		fsync(openFiles[t]);
355 	*/
356 }
357 
getAvailableSize() const358 const CMultiFile::l_addr_t CMultiFile::getAvailableSize() const
359 {
360 	if(!opened)
361 		throw runtime_error(string(__func__)+" -- not opened");
362 
363 	return MAX_OPEN_FILES*LOGICAL_MAX_FILE_SIZE;
364 }
365 
getActualSize() const366 const CMultiFile::l_addr_t CMultiFile::getActualSize() const
367 {
368 	if(!opened)
369 		throw runtime_error(string(__func__)+" -- not opened");
370 
371 	struct stat statBuf;
372 	fstat(openFiles[openFileCount-1],&statBuf);
373 	const l_addr_t sizeOfLastFile=statBuf.st_size;
374 	return ((openFileCount-1)*PHYSICAL_MAX_FILE_SIZE)+sizeOfLastFile;
375 }
376 
getSize() const377 const CMultiFile::l_addr_t CMultiFile::getSize() const
378 {
379 	if(!opened)
380 		throw runtime_error(string(__func__)+" -- not opened");
381 	return totalSize;
382 }
383 
writeHeaderToFiles()384 void CMultiFile::writeHeaderToFiles()
385 {
386 	RFileHeader header;
387 
388 	header.signature=CMULTIFILE_SIGNATURE;
389 	header.matchSignature=matchSignature;
390 	header.fileCount=openFileCount;
391 	header.encodeEndianBeforeWrite();
392 
393 	for(size_t t=0;t<openFileCount;t++)
394 	{
395 		lseek(openFiles[t],0,SEEK_SET);
396 		const ssize_t wroteLength=header.write(openFiles[t]);
397 		if(wroteLength<0)
398 		{
399 			int errNO=errno;
400 			throw runtime_error(string(__func__)+" -- error writing header to file -- strerror: "+strerror(errNO));
401 		}
402 		else if((size_t)wroteLength!=HEADER_SIZE)
403 		{
404 			throw runtime_error(string(__func__)+" -- error writing header to file -- wroteLength/HEADER_SIZE: "+istring(wroteLength)+"/"+istring(HEADER_SIZE));
405 		}
406 	}
407 }
408 
openFile(const string & filename,RFileHeader & header,const bool openingFirstFile,const bool readHeader)409 void CMultiFile::openFile(const string &filename,RFileHeader &header,const bool openingFirstFile,const bool readHeader)
410 {
411 	int fileHandle=-1;
412 	try
413 	{
414 #ifdef WIN32
415 		fileHandle=sopen(filename.c_str(),O_RDWR|O_BINARY,SH_DENYRW);
416 #else
417 		// we need some way of not allowing other processes to open this file once it's open here
418 		fileHandle=::open(filename.c_str(),O_RDWR);
419 #endif
420 		if(fileHandle<0)
421 		{
422 			int errNO=errno;
423 			throw runtime_error(string(__func__)+" -- expected file not found: "+filename+" -- "+strerror(errNO));
424 		}
425 
426 		if(readHeader)
427 		{
428 			// read info which ties this file to other files
429 			if(header.read(fileHandle)==HEADER_SIZE && header.signature==CMULTIFILE_SIGNATURE)
430 			{ // HEADER_SIZE bytes were read and signature matched
431 
432 				if(openingFirstFile)
433 					matchSignature=header.matchSignature;
434 				else if(header.matchSignature!=matchSignature)
435 					throw runtime_error(string(__func__)+" -- matchSignature not match in header information of file: "+filename);
436 
437 				if(header.fileCount>MAX_OPEN_FILES)
438 					throw runtime_error(string(__func__)+" -- fileCount is greater than MAX_OPEN_FILES in header information of file: "+filename);
439 
440 				openFiles[openFileCount++]=fileHandle;
441 				fileHandle=-1;
442 			}
443 			else
444 			{
445 				if(openingFirstFile)
446 				{
447 					// make up new match signature
448 					header.matchSignature=matchSignature=((rand()%256)*256*256*256)+((rand()%256)*256*256)+((rand()%256)*256)+(rand()%256);
449 					header.fileCount=openFileCount=1;
450 
451 					openFiles[0]=fileHandle;
452 					fileHandle=-1;
453 
454 				}
455 				else
456 					throw runtime_error(string(__func__)+" -- invalid header information of file: "+filename);
457 			}
458 		}
459 		else
460 		{
461 			openFiles[openFileCount++]=fileHandle;
462 			fileHandle=-1;
463 		}
464 
465 	}
466 	catch(...)
467 	{
468 		if(fileHandle!=-1)
469 			::close(fileHandle);
470 		throw;
471 	}
472 }
473 
calcTotalSize() const474 const CMultiFile::l_addr_t CMultiFile::calcTotalSize() const
475 {
476 	return getActualSize()-(openFileCount*HEADER_SIZE);
477 }
478 
479 
setFileSize(const int fileHandle,const f_addr_t newFileSize)480 void CMultiFile::setFileSize(const int fileHandle,const f_addr_t newFileSize)
481 {
482 /* Need to use flags from autoconf to set this
483 #if defined(WIN32)
484 	if(chsize(fileHandle,newFileSize)!=0)
485 	{
486 		int err=errno;
487 		printf("error changing block file size: %s\n",strerror(err));
488 		exit(1);
489 	}
490 
491 #elif defined(__SOLARIS_GNU_CPP__) || defined(__SOLARIS_SUN_CPP__) || defined(__LINUX_GNU_CPP__)
492 */
493 	if(ftruncate(fileHandle,newFileSize))
494 	{
495 		const int err=errno;
496 		if(err==EPERM)
497 		{ // "Operation not Permitted" (most likely this is an fat partition, or one that doesn't support sparse files)
498 
499 			// In linux, the FAT file system does not support making the file bigger with ftruncate
500 			// I started a thread in the linux-fsdevel mailing list, but evenutually received thsi reply
501 			//
502 			//     Exactly. We could extended file for you, but we decided that instead of
503 			//     writting zeroes to disk for next couple of hours during one ftruncate()
504 			//     call we leave decision on you - if you really want to extend file, use
505 			//     something else (loop in write and paint some progress bar for impatient
506 			//     user). You can lookup discussion why Al Viro (if my memory serves
507 			//     correctly) decided to not support extending files on filesystems
508 			//     which do not support holes - linux-fsdevel archive should contain it
509 			//     (I think that ~18 months ago, but I'm not sure at all).
510 			//                                         Best regards,
511 			//                                            Petr Vandrovec
512 			//                                            vandrove@vc.cvut.cz
513 
514 			struct stat statBuf;
515 			fstat(fileHandle,&statBuf);
516 			const f_addr_t currentFileSize=statBuf.st_size;
517 			if(currentFileSize<=newFileSize)
518 			{ // write zero to the end of the file
519 				lseek(fileHandle,currentFileSize,SEEK_SET);
520 
521 				const char buffer[1024]={0};
522 				const size_t nChunks=(newFileSize-currentFileSize)/1024;
523 				for(size_t t=0;t<nChunks;t++)
524 				{
525 					if(::write(fileHandle,buffer,1024)!=1024)
526 					{
527 						printf("error changing block file size: %s\n",strerror(errno));
528 						exit(1);
529 					}
530 				}
531 				const ssize_t remainder=(newFileSize-currentFileSize)%1024;
532 				if(remainder>0)
533 				{
534 					if(::write(fileHandle,buffer,remainder)!=remainder)
535 					{
536 						printf("error changing block file size: %s\n",strerror(errno));
537 						exit(1);
538 					}
539 				}
540 
541 				return;
542 			}
543 		}
544 
545 		// ??? may want to seriously consider making this an exception, but if I
546 		// do I need know know all the conditions underwhich it could happen and
547 		// undo whever might assume that the operation was going to succeed
548 		// 	- same for above in two places
549 		printf("error changing block file size: %s\n",strerror(err));
550 		exit(1);
551 	}
552 /*
553 #else
554 	#error no implementation for this platform defined
555 #endif
556 */
557 
558 }
559 
buildFilename(size_t which)560 const string CMultiFile::buildFilename(size_t which)
561 {
562 	return buildFilename(which,initialFilename);
563 }
564 
565 
buildFilename(size_t which,const string & initialFilename)566 const string CMultiFile::buildFilename(size_t which,const string &initialFilename)
567 {
568 	if(which==0)
569 		return initialFilename;
570 	else
571 		return initialFilename+"."+istring(which);
572 }
573 
read(int fd)574 ssize_t CMultiFile::RFileHeader::read(int fd)
575 {
576 	if(::read(fd,&signature,sizeof(signature))!=sizeof(signature))
577 		return 0;
578 	if(::read(fd,&matchSignature,sizeof(matchSignature))!=sizeof(matchSignature))
579 		return 0;
580 	if(::read(fd,&fileCount,sizeof(fileCount))!=sizeof(fileCount))
581 		return 0;
582 
583 	const static int padlen=HEADER_SIZE-(sizeof(signature)+sizeof(matchSignature)+sizeof(fileCount));
584 	if(::lseek(fd,padlen,SEEK_CUR)==(off_t)-1)
585 		return 0;
586 
587 	/* signature is always stored in little-endian */
588 	lethe(&signature);
589 
590 	/* if signature is CMULTIFILE_SIGNATURE+1 then this file was written on a big endian machine */
591 #ifdef WORDS_BIGENDIAN
592 	if(signature==(CMULTIFILE_SIGNATURE+1))
593 	{ // nothing to do, header was written as big endian
594 		signature--;
595 	}
596 	else
597 	{ // header was written on a little endian machine, must convert
598 		lethe(&matchSignature);
599 		lethe(&fileCount);
600 	}
601 #else
602 	if(signature==(CMULTIFILE_SIGNATURE+1))
603 	{ // header was written on a big endian machine, must convert
604 		signature--;
605 
606 		bethe(&signature);
607 		bethe(&matchSignature);
608 		bethe(&fileCount);
609 	}
610 	else
611 	{ // nothing to do, header was written as little endian
612 	}
613 #endif
614 
615 	return HEADER_SIZE;
616 }
617 
write(int fd)618 ssize_t CMultiFile::RFileHeader::write(int fd)
619 {
620 	if(::write(fd,&signature,sizeof(signature))!=sizeof(signature))
621 		return 0;
622 	if(::write(fd,&matchSignature,sizeof(matchSignature))!=sizeof(matchSignature))
623 		return 0;
624 	if(::write(fd,&fileCount,sizeof(fileCount))!=sizeof(fileCount))
625 		return 0;
626 
627 	static int8_t padding[HEADER_SIZE]={0};
628 	const static int padlen=HEADER_SIZE-(sizeof(signature)+sizeof(matchSignature)+sizeof(fileCount));
629 	if(::write(fd,padding,padlen)!=padlen)
630 		return 0;
631 
632 	return HEADER_SIZE;
633 }
634 
encodeEndianBeforeWrite()635 void CMultiFile::RFileHeader::encodeEndianBeforeWrite()
636 {
637 	/* always store signature as little-endian */
638 	/* if we're on a big endian platform add 1 to the signature */
639 #ifdef WORDS_BIGENDIAN
640 	signature++;
641 	hetle(&signature);
642 #else
643 #endif
644 }
645 
646