1 
2 /***************************************************************************
3  *                    __            __ _ ___________                       *
4  *                    \ \          / /| |____   ____|                      *
5  *                     \ \        / / | |    | |                           *
6  *                      \ \  /\  / /  | |    | |                           *
7  *                       \ \/  \/ /   | |    | |                           *
8  *                        \  /\  /    | |    | |                           *
9  *                         \/  \/     |_|    |_|                           *
10  *                                                                         *
11  *                           Wiimms ISO Tools                              *
12  *                         http://wit.wiimm.de/                            *
13  *                                                                         *
14  ***************************************************************************
15  *                                                                         *
16  *   This file is part of the WIT project.                                 *
17  *   Visit http://wit.wiimm.de/ for project details and sources.           *
18  *                                                                         *
19  *   Copyright (c) 2009-2013 by Dirk Clemens <wiimm@wiimm.de>              *
20  *                                                                         *
21  ***************************************************************************
22  *                                                                         *
23  *   This program is free software; you can redistribute it and/or modify  *
24  *   it under the terms of the GNU General Public License as published by  *
25  *   the Free Software Foundation; either version 2 of the License, or     *
26  *   (at your option) any later version.                                   *
27  *                                                                         *
28  *   This program is distributed in the hope that it will be useful,       *
29  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
30  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
31  *   GNU General Public License for more details.                          *
32  *                                                                         *
33  *   See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt       *
34  *                                                                         *
35  ***************************************************************************/
36 
37 #define _GNU_SOURCE 1
38 #define _XOPEN_SOURCE 1
39 
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/ioctl.h>
44 
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <utime.h>
48 #include <ctype.h>
49 #include <dirent.h>
50 
51 #if defined(__CYGWIN__)
52   #include <cygwin/fs.h>
53   #include <io.h>
54   #include "winapi.h"
55 #elif defined(__APPLE__)
56   #include <sys/disk.h>
57 #elif defined(__linux__)
58   #include <linux/fs.h>
59 #endif
60 
61 #include "lib-std.h"
62 #include "lib-sf.h"
63 #include "wbfs-interface.h"
64 
65 ///////////////////////////////////////////////////////////////////////////////
66 
67 #if defined(HAVE_FIEMAP) && defined(FS_IOC_FIEMAP)
68   #undef HAVE_FIEMAP
69   #define HAVE_FIEMAP 1
70   #include <linux/fiemap.h>
71 #else
72   #undef HAVE_FIEMAP
73   #define HAVE_FIEMAP 0
74 #endif
75 
76 #undef HAVE_FIBMAP
77 #if defined(FIGETBSZ) && defined(FIGETBSZ)
78   #define HAVE_FIBMAP 1
79 #else
80   #define HAVE_FIBMAP 0
81 #endif
82 
83 //
84 ///////////////////////////////////////////////////////////////////////////////
85 ///////////////                   file support                  ///////////////
86 ///////////////////////////////////////////////////////////////////////////////
87 
88 int opt_direct = 0;
89 enumIOMode opt_iomode = IOM__IS_DEFAULT | IOM_FORCE_STREAM;
90 
91 //-----------------------------------------------------------------------------
92 
ScanIOMode(ccp arg)93 void ScanIOMode ( ccp arg )
94 {
95     const enumIOMode new_io = strtol(optarg,0,0); // [[2do]] error handling
96     opt_iomode = new_io & IOM__IS_MASK;
97     if ( verbose > 0 || opt_iomode != new_io )
98 	printf("IO mode set to %#0x.\n",opt_iomode);
99     opt_iomode |= IOM_FORCE_STREAM;
100 }
101 
102 ///////////////////////////////////////////////////////////////////////////////
103 
GetHSS(int fd,u32 default_value)104 u32 GetHSS ( int fd, u32 default_value )
105 {
106  #ifdef DKIOCGETBLOCKSIZE
107     TRACE(" - try DKIOCGETBLOCKSIZE\n");
108     {
109 	unsigned long size32 = 0;
110 	if ( ioctl(fd, DKIOCGETBLOCKSIZE, &size32 ) >= 0 && size32 )
111 	{
112 	    TRACE("GetHSS(%d) DKIOCGETBLOCKSIZE := %x = %u\n",fd,size32,size32);
113 	    return size32;
114 	}
115     }
116  #endif
117 
118  #ifdef BLKSSZGET
119     TRACE(" - try BLKSSZGET\n");
120     {
121 	unsigned long size32 = 0;
122 	if ( ioctl(fd, BLKSSZGET, &size32 ) >= 0 && size32 )
123 	{
124 	    TRACE("GetHSS(%d) BLKSSZGET := %lx = %lu\n",fd,size32,size32);
125 	    return size32;
126 	}
127     }
128  #endif
129 
130     TRACE("GetHSS(%d) default_value := %x = %u\n",fd,default_value,default_value);
131     return default_value;
132 }
133 
134 ///////////////////////////////////////////////////////////////////////////////
135 
valid_offset(int fd,off_t off)136 static int valid_offset ( int fd, off_t off )
137 {
138     noTRACE(" - valid_offset(%d,%llu=%#llx)\n",fd,(u64)off,(u64)off);
139     char ch;
140     if (   !off
141 	|| off > ~(off_t)0 / 2
142 	|| lseek(fd,off,SEEK_SET) == (off_t)-1 )
143 	return 0;
144     return read(fd,&ch,1) == 1;
145 }
146 
147 //-----------------------------------------------------------------------------
148 
GetBlockDevSize(int fd)149 off_t GetBlockDevSize ( int fd )
150 {
151     TRACE("GetBlockDevSize(%d)\n",fd);
152 
153  #ifdef BLKGETSIZE64
154     TRACE(" - try BLKGETSIZE64\n");
155     {
156 	unsigned long long size64 = 0;
157 	if ( ioctl(fd, BLKGETSIZE64, &size64 ) >= 0 && size64 )
158 	    return size64;
159     }
160  #endif
161 
162  #ifdef DKIOCGETBLOCKCOUNT
163     TRACE(" - try DKIOCGETBLOCKCOUNT\n");
164     {
165 	unsigned long long size64 = 0;
166 	if ( ioctl(fd, DKIOCGETBLOCKCOUNT, &size64 ) >= 0  && size64 )
167 	    return size64 * GetHSS(fd,HD_SECTOR_SIZE);
168     }
169  #endif
170 
171  #ifdef BLKGETSIZE
172     TRACE(" - try BLKGETSIZE\n");
173     {
174 	unsigned long size32 = 0;
175 	if ( ioctl(fd, BLKGETSIZE, &size32 ) >= 0 && size32 )
176 	    return (off_t)size32 * GetHSS(fd,HD_SECTOR_SIZE);
177     }
178  #endif
179 
180     TRACE(" - try lseek(SEEK_END)\n");
181     {
182 	off_t off = 0;
183 	off = lseek(fd,0,SEEK_END);
184 	if ( off && off != (off_t)-1 )
185 	{
186 	    lseek(fd,0,SEEK_SET);
187 	    return off;
188 	}
189     }
190 
191     TRACE(" - try binary search\n");
192     off_t low, high;
193     for ( low = 0, high = GiB; high > low && valid_offset(fd,high); high *= 2 )
194 	low = high;
195     TRACE(" - low=%llx, high=%llx\n",(u64)low,(u64)high);
196 
197     while ( low < high - 1 )
198     {
199 	const off_t mid = (low + high) / 2;
200 	if (valid_offset(fd,mid))
201 	    low = mid;
202 	else
203 	    high = mid;
204     }
205     lseek(fd,0,SEEK_SET);
206     return low ? low + 1 : 0;
207 }
208 
209 ///////////////////////////////////////////////////////////////////////////////
210 
StatFile(struct stat * st,ccp fname,int fd)211 enumError StatFile ( struct stat * st, ccp fname, int fd )
212 {
213     ASSERT(st);
214 
215     TRACE("StatFile(%s,%d)\n",fname?fname:"",fd);
216 
217     // normalize parameters
218     if ( fname && *fname )
219 	fd = -1;
220     else
221 	fname = 0;
222 
223     if ( fname ? stat(fname,st) : fstat(fd,st) )
224     {
225 	memset(st,0,sizeof(*st));
226 	return ERR_WARNING;
227     }
228 
229     TRACE(" - st_dev:     %llu\n",(u64)st->st_dev);
230     TRACE(" - st_ino:     %llu\n",(u64)st->st_ino);
231     TRACE(" - st_mode:    %llx [REG=%u,DIR=%d,CHR=%d,BLK=%d,FIFO=%d,LNK=%d,SOCK=%d]\n",
232 			(u64)st->st_mode, S_ISREG(st->st_mode),
233 			S_ISDIR(st->st_mode), S_ISCHR(st->st_mode),
234 			S_ISBLK(st->st_mode), S_ISFIFO(st->st_mode),
235 			S_ISLNK(st->st_mode), S_ISSOCK(st->st_mode) );
236     TRACE(" - st_nlink:   %llu\n",(u64)st->st_nlink);
237     TRACE(" - st_uid:     %llu\n",(u64)st->st_uid);
238     TRACE(" - st_gid:     %llu\n",(u64)st->st_gid);
239     TRACE(" - st_rdev:    %llu\n",(u64)st->st_rdev);
240     TRACE(" - st_size:    %10llx = %llu\n",(u64)st->st_size,(u64)st->st_size);
241     TRACE(" - st_blksize: %10llx = %llu\n",(u64)st->st_blksize,(u64)st->st_blksize);
242     TRACE(" - st_blocks:  %10llx = %llu\n",(u64)st->st_blocks,(u64)st->st_blocks);
243     TRACE(" - st_atime:   %llu\n",(u64)st->st_atime);
244     TRACE(" - st_mtime:   %llu\n",(u64)st->st_mtime);
245     TRACE(" - st_ctime:   %llu\n",(u64)st->st_ctime);
246 
247     if ( !st->st_size && GetFileMode(st->st_mode) != FM_OTHER )
248     {
249 	if (fname)
250 	    fd = open(fname,O_RDONLY);
251 
252 	if ( fd != -1 )
253 	{
254 	    if ( GetFileMode(st->st_mode) > FM_PLAIN )
255 	    {
256 		st->st_size = GetBlockDevSize(fd);
257 		TRACE(" + st_size:    %llu\n",(u64)st->st_size);
258 	    }
259 
260 	    if (!st->st_size)
261 	    {
262 		st->st_size = lseek(fd,0,SEEK_END);
263 		TRACE(" + st_size:    %llu\n",(u64)st->st_size);
264 		lseek(fd,0,SEEK_SET);
265 	    }
266 
267 	    if (fname)
268 		close(fd);
269 	}
270     }
271 
272     return ERR_OK;
273 }
274 
275 ///////////////////////////////////////////////////////////////////////////////
276 ///////////////////////////////////////////////////////////////////////////////
277 // initialize, reset and close files
278 
InitializeFile(File_t * f)279 void InitializeFile ( File_t * f )
280 {
281     TRACE("#F# InitializeFile(%p)\n",f);
282     ASSERT(f);
283     ASSERT(ERR_OK==0);
284 
285     memset(f,0,sizeof(*f));
286     f->fd = -1;
287     f->sector_size = 512;
288     f->fname = EmptyString;
289     f->split_fname_format = EmptyString;
290     f->slot = -1;
291     f->already_created_mode = 2;
292 
293     // normalize 'opt_iomode'
294     opt_iomode = opt_iomode & IOM__IS_MASK | IOM_FORCE_STREAM;
295 
296  #ifdef __CYGWIN__
297     opt_iomode |= IOM_IS_WBFS_PART;
298  #endif
299 }
300 
301 ///////////////////////////////////////////////////////////////////////////////
302 
XResetFile(XPARM File_t * f,bool remove_file)303 enumError XResetFile ( XPARM File_t * f, bool remove_file )
304 {
305     TRACE("#F# ResetFile(%p,%d)\n",f,remove_file);
306     ASSERT(f);
307     enumError stat = XClearFile( XCALL f, remove_file );
308 
309     // save user settungs
310     const bool open_flags		= f->open_flags;
311     const bool disable_errors		= f->disable_errors;
312     const bool create_directory		= f->create_directory;
313     const bool allow_direct_io		= f->allow_direct_io;
314     const int  already_created_mode	= f->already_created_mode;
315 
316     InitializeFile(f);
317 
318     // restore user settings
319     f->open_flags		= open_flags;
320     f->disable_errors		= disable_errors;
321     f->create_directory		= create_directory;
322     f->allow_direct_io		= allow_direct_io;
323     f->already_created_mode	= already_created_mode;
324 
325     return stat;
326 }
327 
328 ///////////////////////////////////////////////////////////////////////////////
329 
XClearFile(XPARM File_t * f,bool remove_file)330 enumError XClearFile ( XPARM File_t * f, bool remove_file )
331 {
332     ASSERT(f);
333     TRACE("#F# ClearFile(%p,%d) fd=%d fp=%p\n",f,remove_file,f->fd,f->fp);
334 
335     enumError err = ERR_OK;
336     if (f->split_f)
337     {
338 	File_t **end, **ptr = f->split_f;
339 	for ( end = ptr + f->split_used; ptr < end; ptr++ )
340 	{
341 	    enumError err1 = XClearFile( XCALL *ptr, remove_file );
342 	    FREE(*ptr);
343 	    if ( err < err1 )
344 		err = err1;
345 	}
346 	FREE(f->split_f);
347 	f->split_f = 0;
348 	remove_file = false;
349     };
350     f->split_used = 0;
351 
352     enumError err1 = XCloseFile( XCALL f, remove_file );
353     if ( err < err1 )
354 	err = err1;
355 
356     FreeString(f->fname);		f->fname		= EmptyString;
357     FreeString(f->path);		f->path			= 0;
358     FreeString(f->rename);		f->rename		= 0;
359     FreeString(f->outname);		f->outname		= 0;
360     FreeString(f->split_fname_format);	f->split_fname_format	= EmptyString;
361     FreeString(f->split_rename_format);	f->split_rename_format	= 0;
362 
363     ASSERT(!f->is_caching);
364     ASSERT(!f->cache);
365     ASSERT(!f->cur_cache);
366 
367     f->last_error = err;
368     if ( f->max_error < err )
369 	f->max_error = err;
370     return err;
371 }
372 
373 ///////////////////////////////////////////////////////////////////////////////
374 
XCloseFile(XPARM File_t * f,bool remove_file)375 enumError XCloseFile ( XPARM File_t * f, bool remove_file )
376 {
377     DASSERT(f);
378     TRACE("#F# CloseFile(%p,%d) fd=%d fp=%p\n",f,remove_file,f->fd,f->fp);
379 
380     //----- pralloc support
381 
382     if ( !remove_file && f->prealloc_size > f->max_off && IsOpenF(f) )
383     {
384 	PRINT("PREALLOC/CLOSE: pre=%llx, max=%llx   \n",
385 		(u64)f->prealloc_size, (u64)f->max_off );
386 	XSetSizeF(XCALL f,f->max_off);
387     }
388 
389 
390     //----- close file
391 
392     bool close_err = false;
393     if ( f->fp )
394 	close_err = fclose(f->fp) != 0;
395     else if ( f->fd != -1 )
396 	close_err = close(f->fd) != 0;
397 
398     f->fp =  0;
399     f->fd = -1;
400 
401     enumError err = ERR_OK;
402     if (close_err)
403     {
404 	err = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
405 	if (!f->disable_errors)
406 	    PrintError( XERROR1, err,
407 		"Close file failed: %s\n", f->fname );
408     }
409 
410 
411     //----- split file support & removing & renaming
412 
413     if (f->split_f)
414     {
415 	File_t **end, **ptr = f->split_f;
416 	for ( end = ptr + f->split_used; ptr < end; ptr++ )
417 	{
418 	    TRACE("#S#%zd# close %s\n",ptr-f->split_f,(*ptr)->fname);
419 	    enumError err1 = XCloseFile( XCALL *ptr, remove_file );
420 	    if ( err < err1 )
421 		err = err1;
422 	}
423     }
424     else if ( !f->is_stdfile && S_ISREG(f->st.st_mode) )
425     {
426 	ccp * path = f->path ? &f->path : &f->fname;
427 	if ( remove_file && *path && **path )
428 	{
429 	    TRACE("REMOVE: %s\n",*path);
430 	    unlink(*path);
431 	    FreeString(f->rename);
432 	    f->rename = 0;
433 	}
434 	else if (f->rename)
435 	{
436 	    TRACE("RENAME: %s\n",*path);
437 	    TRACE("    TO: %s\n",f->rename);
438 	    if (rename(*path,f->rename))
439 	    {
440 		if (!f->disable_errors)
441 		    ERROR1( ERR_CANT_CREATE,
442 			"Can't create file: %s\n", f->rename );
443 		if ( err < ERR_CANT_CREATE )
444 		    err = ERR_CANT_CREATE;
445 		unlink(*path);
446 	    }
447 
448 	    FreeString(*path);
449 	    *path = f->rename;
450 	    f->rename = 0;
451 	}
452     }
453 
454 
455     //----- clean
456 
457     ClearCache(f);
458     ResetMemMap(&f->prealloc_map);
459 
460     f->cur_off = f->file_off = 0;
461 
462     f->last_error = err;
463     if ( f->max_error < err )
464 	f->max_error = err;
465 
466     return err;
467 }
468 
469 ///////////////////////////////////////////////////////////////////////////////
470 
XSetFileTime(XPARM File_t * f,FileAttrib_t * set_time)471 enumError XSetFileTime ( XPARM File_t * f, FileAttrib_t * set_time )
472 {
473     // try to change time and ignore error messages
474 
475     ASSERT(f);
476 
477     enumError err = ERR_OK;
478     if ( set_time && set_time->mtime )
479     {
480 	err = XCloseFile( XCALL f, false );
481 
482 	struct timeval tval[2];
483 	tval[0].tv_sec = set_time->atime ? set_time->atime : set_time->mtime;
484 	tval[1].tv_sec = set_time->mtime;
485 	tval[0].tv_usec = tval[1].tv_usec = 0;
486 
487 	if (f->split_f)
488 	{
489 	    File_t **end, **ptr = f->split_f;
490 	    for ( end = ptr + f->split_used; ptr < end; ptr++ )
491 	    {
492 		TRACE("XSetFileTime(%p,%p) fname=%s\n",f,set_time,(*ptr)->fname);
493 		utimes((*ptr)->fname,tval);
494 	    }
495 	}
496 	else
497 	{
498 	    TRACE("XSetFileTime(%p,%p) fname=%s\n",f,set_time,f->fname);
499 	    utimes(f->fname,tval);
500 	}
501     }
502     return err;
503 }
504 
505 ///////////////////////////////////////////////////////////////////////////////
506 ///////////////////////////////////////////////////////////////////////////////
507 
XOpenFileHelper(XPARM File_t * f,enumIOMode iomode,int default_flags,int force_flags)508 static enumError XOpenFileHelper
509 	( XPARM File_t * f, enumIOMode iomode, int default_flags, int force_flags )
510 {
511     ASSERT(f);
512     TRACE("#F# OpenFileHelper(%p,%x,%x,%x) dis=%d, mkdir=%d, fname=%s\n",
513 		f, iomode, default_flags, force_flags,
514 		f->disable_errors, f->create_directory, f->fname );
515 
516  #ifdef O_LARGEFILE
517     TRACE("FORCE O_LARGEFILE\n");
518     force_flags |= O_LARGEFILE;
519  #endif
520 
521  #ifdef O_DIRECT
522     TRACE("FORCE O_DIRECT = %d,%d\n",opt_direct,f->allow_direct_io);
523     if ( opt_direct && f->allow_direct_io )
524 	force_flags |= O_DIRECT;
525  #endif
526 
527     f->active_open_flags = ( f->open_flags ? f->open_flags : default_flags )
528 			 | force_flags;
529 
530     const int mode_mask = O_RDONLY|O_WRONLY|O_RDWR;
531     int mode = f->active_open_flags & mode_mask;
532     if ( !mode || mode == O_RDONLY )
533     {
534 	mode = O_RDONLY;
535 	f->is_reading = true;
536     }
537     else if ( mode == O_WRONLY )
538     {
539 	f->is_writing = true;
540     }
541     else
542     {
543 	mode = O_RDWR;
544 	f->is_reading = true;
545 	f->is_writing = true;
546     }
547     f->active_open_flags = f->active_open_flags & ~mode_mask | mode;
548 
549     if ( f->fd == -1 )
550     {
551 	TRACE("open %s, create-dir=%d\n",f->fname,f->create_directory);
552      #ifdef __CYGWIN__
553 	char * temp = AllocNormalizedFilenameCygwin(f->fname);
554 	TRACE("open %p %p %s\n",f->fname,temp,temp);
555 	//FreeString(f->fname); // [[2do]] [memleak] -> forces a core dump -- mhhm
556 	f->fname = temp;
557      #endif
558 	f->fd = open( f->fname, f->active_open_flags, 0666 );
559 	if ( f->fd == -1 && f->create_directory && f->is_writing )
560 	{
561 	    const enumError err = CreatePath(f->fname);
562 	    if (err)
563 		return err;
564 	    f->fd = open( f->fname, f->active_open_flags, 0666 );
565 	}
566     }
567     TRACE("#F# OpenFile '%s' fd=%d, dis=%d\n", f->fname, f->fd, f->disable_errors );
568 
569     if ( f->fd == -1 )
570     {
571 	if ( f->active_open_flags & O_CREAT )
572 	{
573 	    f->max_error = f->last_error = ERR_CANT_CREATE;
574 	    if (!f->disable_errors)
575 		PrintError( XERROR1, f->last_error,
576 				"Can't create file: %s\n",f->fname);
577 	}
578 	else
579 	{
580 	    f->max_error = f->last_error = ERR_CANT_OPEN;
581 	    if (!f->disable_errors)
582 		PrintError( XERROR1, f->last_error,
583 				"Can't open file: %s\n",f->fname);
584 	}
585     }
586     else if (StatFile(&f->st,0,f->fd))
587     {
588 	close(f->fd);
589 	f->fd = -1;
590 	f->max_error = f->last_error = ERR_READ_FAILED;
591 	if (!f->disable_errors)
592 	    PrintError( XERROR1, f->last_error,"Can't stat file: %s\n",f->fname);
593     }
594     else
595     {
596 	CopyFileAttribStat(&f->fatt,&f->st,false);
597 	f->seek_allowed =  GetFileMode(f->st.st_mode) != FM_OTHER
598 			&& lseek(f->fd,0,SEEK_SET) != (off_t)-1;
599 	if ( iomode & opt_iomode || S_ISCHR(f->st.st_mode) )
600 	    XOpenStreamFile(XCALL f);
601     }
602 
603     TRACE("#F# OpenFileHelper(%p) returns %d, fd=%d, fp=%p, seek-allowed=%d, rw=%d,%d\n",
604 		f, f->last_error, GetFD(f), GetFP(f), f->seek_allowed,
605 		f->is_reading, f->is_writing );
606     return f->last_error;
607 }
608 
609 ///////////////////////////////////////////////////////////////////////////////
610 
XOpenFile(XPARM File_t * f,ccp fname,enumIOMode iomode)611 enumError XOpenFile ( XPARM File_t * f, ccp fname, enumIOMode iomode )
612 {
613     ASSERT(f);
614     TRACE("XOpenFile(%s)\n",fname);
615 
616     const bool no_fname = !fname;
617     if (no_fname)
618     {
619 	fname = f->fname;
620 	f->fname = 0;
621     }
622 
623     XResetFile( XCALL f, false );
624 
625     f->is_stdfile = fname[0] == '-' && !fname[1];
626     if (f->is_stdfile)
627     {
628 	f->fd    = dup(fileno(stdin));
629 	f->fname = STRDUP("- (stdin)");
630     }
631     else
632 	f->fname = no_fname ? fname : STRDUP(fname);
633 
634     return XOpenFileHelper(XCALL f,iomode,O_RDONLY,O_RDONLY);
635 }
636 
637 ///////////////////////////////////////////////////////////////////////////////
638 
XOpenFileModify(XPARM File_t * f,ccp fname,enumIOMode iomode)639 enumError XOpenFileModify ( XPARM File_t * f, ccp fname, enumIOMode iomode )
640 {
641     ASSERT(f);
642 
643     const bool no_fname = !fname;
644     if (no_fname)
645     {
646 	fname = f->fname;
647 	f->fname = 0;
648     }
649 
650     XResetFile( XCALL f, false );
651 
652     f->fname = no_fname ? fname : STRDUP(fname);
653     return XOpenFileHelper(XCALL f,iomode,O_RDWR,O_RDWR);
654 }
655 
656 ///////////////////////////////////////////////////////////////////////////////
657 
XCheckCreated(XPARM ccp fname,bool disable_errors,enumError err_code)658 enumError XCheckCreated
659 	( XPARM ccp fname, bool disable_errors, enumError err_code )
660 {
661     if (!InsertStringField(&created_files,fname,false))
662     {
663 	if (!disable_errors)
664 	    PrintError( XERROR0, err_code,
665 		"File already created: %s\n", fname );
666 	return err_code;
667     }
668     return ERR_OK;
669 }
670 
671 ///////////////////////////////////////////////////////////////////////////////
672 
XCreateFile(XPARM File_t * f,ccp fname,enumIOMode iomode,int overwrite)673 enumError XCreateFile
674 	( XPARM File_t * f, ccp fname, enumIOMode iomode, int overwrite )
675 {
676     ASSERT(f);
677 
678     const int open_flags  = O_CREAT|O_WRONLY|O_TRUNC|O_EXCL;
679     const int force_flags = O_CREAT;
680 
681     if (!fname)
682     {
683 	fname = f->fname;
684 	f->fname = 0;
685     }
686 
687     XResetFile( XCALL f, false );
688 
689     f->is_stdfile = fname[0] == '-' && !fname[1];
690     if (f->is_stdfile)
691     {
692 	f->fd    = dup(fileno(stdout));
693 	f->fname = STRDUP("- (stdout)");
694 
695 	return XOpenFileHelper(XCALL f, iomode, open_flags, force_flags );
696     }
697 
698     if (!stat(fname,&f->st))
699     {
700 	if ( overwrite < 0 || f->disable_errors )
701 	    return ERR_ALREADY_EXISTS;
702 
703 	if ( S_ISBLK(f->st.st_mode) || S_ISCHR(f->st.st_mode) )
704 	{
705 	    if (!overwrite)
706 		return PrintError( XERROR0, ERR_ALREADY_EXISTS,
707 		    "Can't overwrite block device: %s\n", fname );
708 
709 	    f->fname = STRDUP(fname);
710 	    return XOpenFileHelper(XCALL f, iomode, O_WRONLY, 0 );
711 	}
712 	else
713 	{
714 	    if (!S_ISREG(f->st.st_mode))
715 		return PrintError( XERROR0, ERR_WRONG_FILE_TYPE,
716 		    "Not a plain file: %s\n", fname );
717 
718 	    if (!overwrite)
719 		return PrintError( XERROR0, ERR_ALREADY_EXISTS,
720 		    "File already exists: %s\n", fname );
721 	}
722     }
723 
724     TRACE("#F# CREATE: '%s' mkdir=%d\n",fname,f->create_directory);
725 
726     int flen = strlen(fname);
727     if ( flen >= PATH_MAX )
728     {
729 	// no temp name possible
730 
731 	f->fname = STRDUP(fname);
732 	return XOpenFileHelper(XCALL f, iomode, open_flags, force_flags );
733     }
734 
735     const bool disable_errors = f->disable_errors;
736     TRACE("dis=%d\n",disable_errors);
737     f->disable_errors = true;
738 
739     char fbuf[PATH_MAX+20], *XXXXXX;
740     if ( flen >= 7 && !memcmp(fname+flen-7,".XXXXXX",7) )
741     {
742 	memcpy(fbuf,fname,flen);
743 	fbuf[flen] = 0;
744 	XXXXXX = fbuf + flen - 6;
745     }
746     else if ( flen >= 11 && !memcmp(fname+flen-11,".XXXXXX.tmp",11) )
747     {
748 	memcpy(fbuf,fname,flen);
749 	fbuf[flen] = 0;
750 	XXXXXX = fbuf + flen - 10;
751     }
752     else
753     {
754 	if ( f->already_created_mode > 1 )
755 	{
756 	    if (XCheckCreated(XCALL fname,disable_errors,ERR_CANT_CREATE))
757 		goto abort;
758 	}
759 	else if ( f->already_created_mode > 0 )
760 	    XCheckCreated(XCALL fname,disable_errors,ERR_WARNING);
761 
762 	f->rename = STRDUP(fname);
763 
764 	ccp name = strrchr(fname,'/');
765 	name = name ? name+1 : fname;
766 	memcpy(fbuf,fname,name-fname);
767 	char * dest = fbuf + (name-fname);
768 	ASSERT( dest < fbuf + PATH_MAX );
769 
770 	char * dest_name = dest;
771 	*dest++ = '.';
772 	dest = StringCopyE(dest,fbuf+PATH_MAX,name);
773 	if ( dest - dest_name > 50 )
774 	     dest = dest_name + 50;
775 	XXXXXX = dest + 1;
776 	StringCopyE(dest,fbuf+sizeof(fbuf),".XXXXXX.tmp");
777 	TRACE("#F# TEMP:   '%s'\n",fbuf);
778     }
779 
780 
781     //---------------------------------------------------------
782     // I have seen the glibc function __gen_tempname() ;)
783     //---------------------------------------------------------
784 
785     struct timeval tval;
786     gettimeofday(&tval,NULL);
787     u64 random_time_bits = (u64) tval.tv_usec << 16 ^ tval.tv_sec;
788     u64 value = random_time_bits ^ getpid();
789 
790     static char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
791 			    "abcdefghijklmnopqrstuvwxyz"
792 			    "0123456789";
793 
794     int i;
795     for ( i = 0; i < 100; value += 7777, i++ )
796     {
797 	u64 v = value;
798 	XXXXXX[0] = letters[v%62]; v /= 62;
799 	XXXXXX[1] = letters[v%62]; v /= 62;
800 	XXXXXX[2] = letters[v%62]; v /= 62;
801 	XXXXXX[3] = letters[v%62]; v /= 62;
802 	XXXXXX[4] = letters[v%62]; v /= 62;
803 	XXXXXX[5] = letters[v%62];
804 
805 	f->fname = fbuf; // temporary assignment
806 	const enumError err = XOpenFileHelper(XCALL f,iomode,open_flags,open_flags);
807 	if ( err == ERR_OK || err == ERR_CANT_CREATE_DIR )
808 	{
809 	    noPRINT("#F# TEMP:   '%s'\n",fbuf);
810 	    f->fname = STRDUP(fbuf);
811 	    f->disable_errors = disable_errors;
812 	    return f->last_error = f->max_error = err;
813 	}
814 	f->fname = 0;
815 	XResetFile( XCALL f, false );
816     }
817 
818     // cleanup
819     f->fname = EmptyString;
820     memset(&f->st,0,sizeof(f->st));
821 
822     if (!disable_errors)
823 	PrintError( XERROR0, ERR_CANT_CREATE,
824 			"Can't create temp file: %s\n", fname );
825 
826  abort:
827     f->max_error = f->last_error = ERR_CANT_CREATE;
828     f->disable_errors = disable_errors;
829     TRACE("#F# CreateFile(%p) returns %d, fd=%d, fp=%p\n",
830 		f, f->last_error, f->fd, f->fp );
831     return f->last_error;
832 }
833 
834 ///////////////////////////////////////////////////////////////////////////////
835 
XOpenStreamFile(XPARM File_t * f)836 enumError XOpenStreamFile ( XPARM File_t * f )
837 {
838     ASSERT(f);
839     TRACE("#F# OpenStreamFile(%p) fd=%d, fp=%p\n",f,f->fd,f->fp);
840 
841     f->last_error = 0;
842     if ( f->fd != -1 || !f->fp )
843     {
844 	// flag 'b' is set for compatibilitiy only, linux ignores it
845 	ccp mode = !f->is_writing ? "rb" : f->is_reading ? "r+b" : "wb";
846 	TRACE("#F# open mode: '%s'\n",mode);
847 	f->fp = fdopen(f->fd,mode);
848 	if (!f->fp)
849 	{
850 	    f->last_error = ERR_CANT_OPEN;
851 	    if ( f->max_error < f->last_error )
852 		f->max_error = f->last_error;
853 	    if (!f->disable_errors)
854 		PrintError( XERROR1,  f->last_error, "Can't open file stream: %s\n",
855 			f->fname );
856 	}
857 	else
858 	    SeekF(f,f->file_off);
859     }
860 
861     noTRACE("#F# OpenStreamFile(%p) returns %d, fd=%d, fp=%p, off=%llx\n",
862 		f, f->last_error, GetFD(f), GetFP(f), (u64)f->file_off );
863     return f->last_error;
864 }
865 
866 //
867 ///////////////////////////////////////////////////////////////////////////////
868 ///////////////////////////////////////////////////////////////////////////////
869 // file name generation
870 
NormalizeFileName(char * buf,char * end,ccp source,bool allow_slash)871 char * NormalizeFileName ( char * buf, char * end, ccp source, bool allow_slash )
872 {
873     ASSERT(buf);
874     ASSERT(end);
875     ASSERT( buf <= end );
876     char * dest = buf;
877     TRACE("NormalizeFileName(%s,%d)\n",source,allow_slash);
878 
879     if (source)
880     {
881      #ifdef __CYGWIN__
882 	if (allow_slash)
883 	{
884 	    const int drv_len = IsWindowsDriveSpec(source);
885 	    if (drv_len)
886 	    {
887 		dest = StringCopyE(dest,end,"/cygdrive/c/");
888 		if ( dest < end )
889 		    dest[-2] = tolower((int)*source);
890 		source += drv_len;
891 	    }
892 	}
893      #endif
894 
895 	bool skip_space = true;
896 	while ( *source && dest < end )
897 	{
898 	    unsigned char ch = *source++;
899 	    if ( ch == ':' )
900 	    {
901 		if (!skip_space)
902 		    *dest++ = ' ';
903 		if ( dest + 2 <= end )
904 		{
905 		    *dest++ = '-';
906 		    *dest++ = ' ';
907 		}
908 		skip_space = true;
909 	    }
910 	    else
911 	    {
912 		if ( isalnum(ch)
913 			|| ( use_utf8
914 				? ch >= 0x80
915 				:    ch == 0xe4 // ä
916 				  || ch == 0xf6 // ö
917 				  || ch == 0xfc // ü
918 				  || ch == 0xdf // ß
919 				  || ch == 0xc4 // A
920 				  || ch == 0xd6 // Ö
921 				  || ch == 0xdc // Ü
922 			    )
923 			|| strchr("_+-=%'\"$%&,.!()[]{}<>",ch)
924 			|| ch == '/' && allow_slash )
925 		{
926 		    *dest++ = ch;
927 		    skip_space = false;
928 		}
929 	     #ifdef __CYGWIN__
930 		else if ( ch == '\\' && allow_slash )
931 		{
932 		    *dest++ = '/';
933 		    skip_space = false;
934 		}
935 	     #endif
936 		else if (!skip_space)
937 		{
938 		    *dest++ = ' ';
939 		    skip_space = true;
940 		}
941 	    }
942 	}
943     }
944     if ( dest > buf && dest[-1] == ' ' )
945 	dest--;
946 
947     ASSERT( dest <= end );
948     return dest;
949 }
950 
951 ///////////////////////////////////////////////////////////////////////////////
952 
ReduceToPathAndType(char * buf,uint buf_size,ccp fname)953 uint ReduceToPathAndType
954 (
955     char	*buf,		// valid return buffer
956     uint	buf_size,	// size of 'buf'
957     ccp		fname		// source: file name
958 )
959 {
960     DASSERT(buf);
961     DASSERT(buf_size>1);
962     DASSERT(fname);
963 
964     StringCopyS(buf,buf_size,fname);
965     char *dest = strrchr(buf,'/');
966     dest = dest ? dest+1 : buf;
967     ccp src = strrchr(fname,'.');
968     if ( src && src > fname )
969     {
970 	*dest++ = '*';
971 	while ( ( *dest++ = *src++ ) != 0 )
972 	    ;
973 	DASSERT( dest < buf + buf_size );
974 	*dest = 0;
975 	return buf - dest;
976     }
977 
978     *buf = 0;
979     return 0;
980 }
981 
982 ///////////////////////////////////////////////////////////////////////////////
983 
SetFileName(File_t * f,ccp source,bool allow_slash)984 void SetFileName ( File_t * f, ccp source, bool allow_slash )
985 {
986     char fbuf[PATH_MAX];
987     char * dest = NormalizeFileName(fbuf, fbuf+sizeof(fbuf)-1, source, allow_slash );
988     *dest++ = 0;
989     const int len = dest - fbuf;
990 
991     FreeString(f->fname);
992     if ( len <= 1 )
993 	f->fname = EmptyString;
994     else
995     {
996 	dest = MALLOC(len);
997 	memcpy(dest,fbuf,len);
998 	f->fname = dest;
999     }
1000 }
1001 
1002 ///////////////////////////////////////////////////////////////////////////////
1003 
GenFileName(File_t * f,ccp path,ccp name,ccp title,ccp id6,ccp ext)1004 void GenFileName ( File_t * f, ccp path, ccp name, ccp title, ccp id6, ccp ext )
1005 {
1006     ASSERT(f);
1007 
1008     TRACE("GenFileName(,p=%s,n=%s,i=%s,e=%s)\n",
1009 		path ? path : "",
1010 		name ? name : "",
1011 		id6  ? id6  : "",
1012 		ext  ? ext  : "" );
1013 
1014     FreeString(f->fname);
1015     f->fname = EmptyString;
1016 
1017     //----- check stdout
1018 
1019     f->is_stdfile = name && name[0] == '-' && !name[1];
1020     if (f->is_stdfile)
1021     {
1022 	f->fname = MinusString;
1023 	return;
1024     }
1025 
1026     //----- evaluate 'path'
1027 
1028     char fbuf[PATH_MAX+20];
1029 
1030     if ( name && *name == '/' )
1031 	path = 0;
1032 
1033     int plen = path ? strlen(path) : 0;
1034     if ( plen > PATH_MAX )
1035     {
1036 	if (!realpath(path,fbuf))
1037 	    strcpy(fbuf,"./");
1038 	plen = strlen(fbuf);
1039     }
1040     else if (plen)
1041 	memcpy(fbuf,path,plen);
1042 
1043     char * dest = fbuf + plen;
1044     if ( plen && dest[-1] != '/' )
1045 	*dest++ = '/';
1046 
1047     TRACE(" >PATH: |%.*s|\n",(int)(dest-fbuf),fbuf);
1048 
1049     //----- evaluate 'name'
1050 
1051     char * end  = fbuf + sizeof(fbuf) -  15; // space for "[123456].ext"
1052 
1053     if (name)
1054     {
1055 	// just copy it
1056 	dest = StringCopyE(dest,end,name);
1057 	TRACE(" >NAME: |%.*s|\n",(int)(dest-fbuf),fbuf);
1058     }
1059     else
1060     {
1061 	dest = NormalizeFileName(dest,end,title,false);
1062 	TRACE(" >TITL: |%.*s|\n",(int)(dest-fbuf),fbuf);
1063 
1064 	if ( id6 && *id6 )
1065 	{
1066 	    dest += snprintf(dest,end-dest,"%s[%s]",
1067 			dest > fbuf && dest[-1] == '/' ? "" : " ", id6 );
1068 	    TRACE(" >ID6:  |%.*s|\n",(int)(dest-fbuf),fbuf);
1069 	}
1070     }
1071 
1072     //----- ext handling
1073 
1074     if (ext)
1075     {
1076 	const int elen = strlen(ext);
1077 	if ( elen > 0
1078 	    && ( dest - elen <= fbuf || memcmp(dest-elen,ext,elen) )
1079 	    && dest + elen < end )
1080 	{
1081 	    TRACE("add ext %s\n",ext);
1082 	    memcpy(dest,ext,elen);
1083 	    dest += elen;
1084 	    TRACE(" >EXT:  |%.*s|\n",(int)(dest-fbuf),fbuf);
1085 	}
1086     }
1087 
1088     //----- term
1089 
1090     *dest = 0;
1091     f->fname = STRDUP(fbuf);
1092     TRACE("FNAME:      %s\n",fbuf);
1093 }
1094 
1095 ///////////////////////////////////////////////////////////////////////////////
1096 
GenDestFileName(File_t * f,ccp dest,ccp default_name,ccp ext,bool rm_std_ext)1097 void GenDestFileName
1098 	( File_t * f, ccp dest, ccp default_name, ccp ext, bool rm_std_ext )
1099 {
1100     char fbuf[PATH_MAX];
1101     if ( rm_std_ext && default_name )
1102     {
1103 	const size_t name_len = strlen(default_name);
1104 	bool done = false;
1105 	enumOFT oft;
1106 	for ( oft = 0; oft < OFT__N && !done; oft++ )
1107 	{
1108 	    int i;
1109 	    for ( i = 0; i < 2 && !done; i++ )
1110 	    {
1111 		ccp rm = i ? oft_info[oft].ext2 : oft_info[oft].ext1;
1112 		if (!rm)
1113 		    continue;
1114 
1115 		const size_t rm_len = strlen(rm);
1116 		const size_t cut_len = name_len - rm_len;
1117 		if ( rm_len && rm_len <= name_len && !strcasecmp(rm,default_name+cut_len) )
1118 		{
1119 		    // got it!
1120 
1121 		    if ( cut_len < sizeof(fbuf) )
1122 		    {
1123 			memcpy(fbuf,default_name,cut_len);
1124 			fbuf[cut_len] = 0;
1125 			default_name = fbuf;
1126 		    }
1127 		    done = true; // remove maximal 1 extension
1128 		}
1129 	    }
1130 	}
1131     }
1132 
1133     if ( IsDirectory(dest,true) )
1134     {
1135 	if ( dest && *dest && default_name )
1136 	{
1137 	    ccp file_part = strrchr(default_name,'/');
1138 	    if ( file_part )
1139 		default_name = file_part + 1;
1140 	}
1141 	GenFileName(f,dest,default_name,0,0,ext);
1142     }
1143     else
1144 	GenFileName(f,0,dest,0,0,0);
1145 }
1146 
1147 ///////////////////////////////////////////////////////////////////////////////
1148 
GenImageFileName(File_t * f,ccp dest,ccp default_name,enumOFT oft)1149 void GenImageFileName ( File_t * f, ccp dest, ccp default_name, enumOFT oft )
1150 {
1151     ccp ext = (uint)oft < OFT__N ? oft_info[oft].ext1 : 0;
1152     GenDestFileName(f,dest,default_name,ext,true);
1153 }
1154 
1155 ///////////////////////////////////////////////////////////////////////////////
1156 
1157 const OFT_info_t oft_info[OFT__N+1] =
1158 {
1159     // HINT: The extra 0 in "\0" is needed because of: oft_info[].ext1+1
1160 
1161     // { oft, attrib, iom,
1162     //     name, option, ext1, ext2, info },
1163 
1164     { OFT_UNKNOWN,
1165 	0,
1166 	IOM__IS_DEFAULT,
1167 	"?", 0, "\0", 0, "unkown file format" },
1168 
1169     { OFT_PLAIN,
1170 	OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY|OFT_A_LOADER,
1171 	IOM_IS_IMAGE,
1172 	"ISO", "--iso", ".iso", 0, "plain file" },
1173 
1174     { OFT_WDF,
1175 	OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY,
1176 	IOM_IS_IMAGE,
1177 	"WDF", "--wdf", ".wdf", 0, "Wii Disc Format" },
1178 
1179     { OFT_CISO,
1180 	OFT_A_READ|OFT_A_WRITE|OFT_A_MODIFY|OFT_A_NOSIZE|OFT_A_LOADER,
1181 	IOM_IS_IMAGE,
1182 	"CISO", "--ciso", ".ciso", ".wbi", "Compact ISO" },
1183 
1184     { OFT_WBFS,
1185 	OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY|OFT_A_NOSIZE|OFT_A_LOADER,
1186 	IOM_IS_IMAGE,
1187 	"WBFS", "--wbfs", ".wbfs", 0, "Wii Backup File System" },
1188 
1189     { OFT_WIA,
1190 	OFT_A_READ|OFT_A_WRITE|OFT_A_COMPR,
1191 	IOM_IS_WIA,
1192 	"WIA", "--wia", ".wia", 0, "compressed Wii ISO Archive" },
1193 
1194     { OFT_FST,
1195 	OFT_A_READ|OFT_A_WRITE|OFT_A_FST,
1196 	IOM__IS_DEFAULT,
1197 	"FST", "--fst", "\0", 0, "extracted File System" },
1198 
1199     { OFT__N,
1200 	0,
1201 	IOM__IS_DEFAULT,
1202 	0, 0, 0, 0, 0 },
1203 };
1204 
1205 ///////////////////////////////////////////////////////////////////////////////
1206 
1207 const CommandTab_t ImageTypeTab[] =
1208 {
1209     { OFT_PLAIN,	"ISO",	"PLAIN",	0 },
1210     { OFT_WDF,		"WDF",	0,		0 },
1211     { OFT_CISO,		"CISO",	0,		0 },
1212     { OFT_WBFS,		"WBFS",	0,		0 },
1213     { OFT_WIA,		"ISO",	0,		0 },
1214     { OFT_FST,		"FST",	0,		1 },
1215     { 0,0,0,0 }
1216 };
1217 
1218 ///////////////////////////////////////////////////////////////////////////////
1219 
CalcOFT(enumOFT force,ccp fname_dest,ccp fname_src,enumOFT def)1220 enumOFT CalcOFT ( enumOFT force, ccp fname_dest, ccp fname_src, enumOFT def )
1221 {
1222     if ( force > OFT_UNKNOWN && force < OFT__N )
1223 	return force;
1224 
1225     ccp fname = IsDirectory(fname_dest,true) ? fname_src : fname_dest;
1226     if (fname)
1227     {
1228 	const size_t len = strlen(fname);
1229 	if ( len >= 4 )
1230 	{
1231 	    if ( !strcasecmp(fname+len-4,".wdf") )
1232 		return OFT_WDF;
1233 
1234 	    if ( !strcasecmp(fname+len-4,".wia") )
1235 		return OFT_WIA;
1236 
1237 	    if ( !strcasecmp(fname+len-4,".iso") )
1238 		return OFT_PLAIN;
1239 
1240 	    if ( !strcasecmp(fname+len-4,".wbi") )
1241 		return OFT_CISO;
1242 
1243 	    if ( len >= 5 )
1244 	    {
1245 		if ( !strcasecmp(fname+len-5,".wbfs") )
1246 		    return OFT_WBFS;
1247 
1248 		if ( !strcasecmp(fname+len-5,".ciso") )
1249 		    return OFT_CISO;
1250 	    }
1251 	}
1252     }
1253 
1254     return def;
1255 }
1256 
1257 //
1258 ///////////////////////////////////////////////////////////////////////////////
1259 ///////////////			split file support		///////////////
1260 ///////////////////////////////////////////////////////////////////////////////
1261 
ExtractSplitMap(File_t * f,File_t * f2)1262 static void ExtractSplitMap
1263 (
1264     File_t		* f,		// main file
1265     File_t		* f2		// split file
1266 )
1267 {
1268     DASSERT(f);
1269     DASSERT(f2);
1270 
1271     if ( !f2->prealloc_done && f->prealloc_map.used )
1272     {
1273 	const off_t split_end = f2->split_off + f2->split_filesize;
1274 	MemMapItem_t ** end_field = f->prealloc_map.field + f->prealloc_map.used;
1275 	MemMapItem_t **ptr;
1276 	for ( ptr = f->prealloc_map.field; ptr < end_field; ptr++ )
1277 	{
1278 	    off_t beg = (*ptr)->off;
1279 	    off_t end = beg + (*ptr)->size;
1280 	    if ( beg < split_end && end > f2->split_off )
1281 	    {
1282 		if ( beg < f2->split_off )
1283 		     beg = f2->split_off;
1284 		if ( end > split_end )
1285 		     end = split_end;
1286 		PRINT(">>> PREALLOC: fd=%u, %9llx .. %9llx\n",f2->fd,(u64)beg,(u64)end);
1287 		MemMapItem_t * item
1288 		    = InsertMemMapTie(&f2->prealloc_map,beg-f2->split_off,end-beg);
1289 		DASSERT(item);
1290 		snprintf(item->info,sizeof(item->info),
1291 			"v-off=%llx, %s\n",
1292 			(u64)beg, wd_print_size(0,0,end-beg,false,WD_SIZE_AUTO) );
1293 	    }
1294 	}
1295     }
1296 }
1297 
1298 ///////////////////////////////////////////////////////////////////////////////
1299 
XSetupSplitFile(XPARM File_t * f,enumOFT oft,off_t split_size)1300 enumError XSetupSplitFile ( XPARM File_t *f, enumOFT oft, off_t split_size )
1301 {
1302     ASSERT(f);
1303     if (f->split_f)
1304 	return ERR_OK;
1305 
1306     if ( f->fd == -1 || !S_ISREG(f->st.st_mode) )
1307     {
1308 	if (!f->disable_errors)
1309 	    ERROR0(ERR_WARNING,
1310 		"Split file support not possible: %s\n", f->fname );
1311 	return ERR_WARNING;
1312     }
1313 
1314     // at very first: setup split file format
1315 
1316     FreeString(f->split_fname_format);
1317     FreeString(f->split_rename_format);
1318     if (f->rename)
1319     {
1320 	f->split_fname_format = AllocSplitFilename(f->fname,OFT_PLAIN);
1321 	f->split_rename_format = AllocSplitFilename(f->rename,oft);
1322     }
1323     else
1324 	f->split_fname_format = AllocSplitFilename(f->fname,oft);
1325 
1326     char fname[PATH_MAX];
1327 
1328     if (!f->is_writing)
1329     {
1330 	// for read only files: test if split files available
1331 	snprintf(fname,sizeof(fname),f->split_fname_format,1);
1332 	struct stat st;
1333 	if (stat(fname,&st))
1334 	{
1335 	    bool found = false;
1336 	    const int slen =  strlen(f->split_fname_format);
1337 	    if ( slen > 2 && f->split_fname_format[slen-2] == '1' )
1338 	    {
1339 		int fw;
1340 		for ( fw = '2'; fw <= '6'; fw++ )
1341 		{
1342 		    ((char*)f->split_fname_format)[slen-2] = fw;
1343 		    snprintf(fname,sizeof(fname),f->split_fname_format,1);
1344 		    if (!stat(fname,&st))
1345 		    {
1346 			found = true;
1347 			break;
1348 		    }
1349 		}
1350 	    }
1351 	    if (!found)
1352 		return ERR_OK; // no split files found -> return
1353 	}
1354 	noPRINT("SPLIT-FNAME: %s\n",f->split_fname_format);
1355     }
1356 
1357     File_t ** list = CALLOC(MAX_SPLIT_FILES,sizeof(*list));
1358     File_t * first;
1359     *list = first = MALLOC(sizeof(File_t));
1360 
1361     // copy all but cache
1362     memcpy(first,f,sizeof(File_t));
1363 
1364     first->is_caching = false;
1365     first->cache = 0;
1366     first->cur_cache = 0;
1367 
1368     const bool have_stream = f->fp != 0;
1369     f->fp =  0;
1370     f->fd = -1;
1371     f->split_f = list;
1372     f->split_used = 1;
1373 
1374     if (f->rename)
1375     {
1376 	// fname is only informative -> use the final name
1377 	f->fname = STRDUP(f->rename);
1378 	f->rename = 0;
1379     }
1380     else
1381 	f->fname = STRDUP(f->fname);
1382     if (f->path)
1383 	f->path = STRDUP(f->path);
1384 
1385     if ( oft == OFT_PLAIN || oft == OFT_CISO || oft == OFT_WBFS )
1386     {
1387 	f->split_filesize = split_size ? split_size : DEF_SPLIT_SIZE_ISO;
1388 	if ( DEF_SPLIT_FACTOR_ISO > 0 )
1389 	{
1390 	    f->split_filesize &= ~(DEF_SPLIT_FACTOR_ISO-1);
1391 	    if (!f->split_filesize)
1392 		f->split_filesize = DEF_SPLIT_FACTOR_ISO;
1393 	}
1394     }
1395     else
1396     {
1397 	f->split_filesize  = split_size ? split_size : DEF_SPLIT_SIZE;
1398 	if ( DEF_SPLIT_FACTOR > 0 )
1399 	{
1400 	    f->split_filesize &= ~(DEF_SPLIT_FACTOR-1);
1401 	    if (!f->split_filesize)
1402 		f->split_filesize = DEF_SPLIT_FACTOR;
1403 	}
1404     }
1405 
1406     first->split_filesize = first->st.st_size;
1407     first->split_fname_format = EmptyString;
1408     first->split_rename_format = 0;
1409     first->outname = 0;
1410 
1411     PRINT("#S#   Split setup, size=%llu, fname=%s\n",
1412 	(u64)f->split_filesize, f->fname );
1413     PRINT("#S#0# split setup, size=%llu, fname=%s\n",
1414 	(u64)first->split_filesize, first->fname );
1415 
1416     noTRACE(" fname:   %p %p\n",f->fname,first->fname);
1417     noTRACE(" path:    %p %p\n",f->path,first->path);
1418     noTRACE(" rename:  %p %p\n",f->rename,first->rename);
1419     noTRACE(" outname: %p %p\n",f->outname,first->outname);
1420     noTRACE(" split-fname-format:  %s\n",f->split_fname_format);
1421     noTRACE(" split-rename-format: %s\n",f->split_rename_format);
1422 
1423     ExtractSplitMap(f,first);
1424 
1425     if (f->is_reading)
1426     {
1427 	int idx;
1428 	for ( idx = 1; idx < MAX_SPLIT_FILES; idx++ )
1429 	{
1430 	    snprintf(fname,sizeof(fname),f->split_fname_format,idx);
1431 	    struct stat st;
1432 	    if (stat(fname,&st))
1433 		break;
1434 
1435 	    File_t * fi = MALLOC(sizeof(*f));
1436 	    InitializeFile(fi);
1437 	    fi->open_flags = f->active_open_flags;
1438 	    fi->fname = STRDUP(fname);
1439 	    enumError err = XOpenFileHelper( XCALL fi,
1440 				have_stream ? IOM_FORCE_STREAM : IOM_NO_STREAM,
1441 				f->active_open_flags, f->active_open_flags );
1442 	    if (err)
1443 	    {
1444 		CloseFile(fi,false);
1445 		FREE(fi);
1446 		return err;
1447 	    }
1448 
1449 	    if (f->split_rename_format)
1450 	    {
1451 		ASSERT(!fi->rename);
1452 		snprintf(fname,sizeof(fname),f->split_rename_format,idx);
1453 		fi->rename = STRDUP(fname);
1454 	    }
1455 
1456 	    fi->split_off = f->split_f[idx-1]->split_off
1457 			  + f->split_f[idx-1]->split_filesize;
1458 	    fi->split_filesize = fi->st.st_size;
1459 
1460 	    PRINT("#S#%u# Open %s, soff=%llx, ssize=%llx\n",
1461 			idx, fname, (u64)fi->split_off, (u64)fi->split_filesize );
1462 	    f->st.st_size += fi->st.st_size;
1463 	    f->split_f[idx] = fi;
1464 	}
1465 	f->split_used = idx;
1466 	f->fatt.size = f->st.st_size;
1467     }
1468 
1469     File_t * fi = f->split_f[f->split_used-1];
1470     ASSERT(fi);
1471     if ( fi->split_filesize < f->split_filesize )
1472     {
1473 	TRACE("#S#%u# new ssize: %llx -> %llx\n",
1474 		f->split_used-1, (u64)fi->split_filesize, (u64)f->split_filesize );
1475 	fi->split_filesize = f->split_filesize;
1476     }
1477 
1478  #ifdef DEBUG
1479     {
1480 	int i;
1481 	for ( i = 0; i < f->split_used; i++ )
1482 	    TRACE(" - %2d: ssize= %9llx\n",i,(u64)f->split_f[i]->split_filesize);
1483     }
1484  #endif
1485 
1486     return ERR_OK;
1487 }
1488 
1489 ///////////////////////////////////////////////////////////////////////////////
1490 
XCreateSplitFile(XPARM File_t * f,uint split_idx)1491 enumError XCreateSplitFile ( XPARM File_t *f, uint split_idx )
1492 {
1493     ASSERT( f );
1494     ASSERT( f->split_f );
1495     ASSERT( split_idx > 0 );
1496     TRACE("#S# CreateSplitFile() %u/%u/%u\n",split_idx,f->split_used,MAX_SPLIT_FILES);
1497 
1498     if ( split_idx > MAX_SPLIT_FILES )
1499     {
1500 	if ( !f->disable_errors )
1501 	    PrintError( XERROR1, ERR_WRITE_FAILED,
1502 			"Max number of split files (%d,off=%llx) reached: %s\n",
1503 			MAX_SPLIT_FILES, (u64)f->file_off, f->fname );
1504 
1505 	f->last_error = ERR_WRITE_FAILED;
1506 	if ( f->max_error < f->last_error )
1507 	    f->max_error = f->last_error;
1508 	return ERR_WRITE_FAILED;
1509     }
1510 
1511     while ( f->split_used <= split_idx )
1512     {
1513 	File_t ** ptr = f->split_f + f->split_used;
1514 	ASSERT(!*ptr);
1515 
1516 	File_t * prev;
1517 	if ( f->split_used > 0 )
1518 	{
1519 	    prev = ptr[-1];
1520 	    if ( prev->is_writing )
1521 		SetSizeF(prev,prev->split_filesize);
1522 	}
1523 	else
1524 	    prev = 0;
1525 
1526 	char fname[PATH_MAX];
1527 	snprintf(fname,sizeof(fname),f->split_fname_format,f->split_used++);
1528 	TRACE("#S#%u# Create %s\n",f->split_used-1,fname);
1529 
1530 	File_t * f2 = MALLOC(sizeof(File_t));
1531 	InitializeFile(f2);
1532 	*ptr = f2;
1533 	const int flags = O_CREAT|O_WRONLY|O_TRUNC|O_EXCL|f->active_open_flags;
1534 	f2->fname = STRDUP(fname);
1535 	enumError err
1536 	    = XOpenFileHelper( XCALL f2,
1537 				prev && prev->fp ? IOM_FORCE_STREAM : IOM_NO_STREAM,
1538 				flags, flags );
1539 	if (err)
1540 	    return err;
1541 	if (prev)
1542 	    f2->split_off = prev->split_off + prev->split_filesize;
1543 	f2->split_filesize = f->split_filesize;
1544 	PRINT("SPLIT #%u: %9llx + %9llx\n",
1545 		split_idx, (u64)f2->split_off,(u64)f2->split_filesize);
1546 	if (f->split_rename_format)
1547 	{
1548 	    ASSERT(!f2->rename);
1549 	    snprintf(fname,sizeof(fname),f->split_rename_format,f->split_used-1);
1550 	    f2->rename = STRDUP(fname);
1551 	}
1552 
1553 	ExtractSplitMap(f,f2);
1554     }
1555     return ERR_OK;
1556 }
1557 
1558 ///////////////////////////////////////////////////////////////////////////////
1559 
XFindSplitFile(XPARM File_t * f,uint * p_index,off_t * p_off)1560 enumError XFindSplitFile ( XPARM File_t *f, uint * p_index, off_t * p_off )
1561 {
1562     ASSERT( f );
1563     ASSERT( f->split_f );
1564     ASSERT(p_index);
1565     ASSERT(p_off);
1566     off_t off = *p_off;
1567     TRACE("#S# XFindSplitFile(off=%llx) %u/%u\n",(u64)off,f->split_used,MAX_SPLIT_FILES);
1568 
1569     File_t ** ptr = f->split_f;
1570     for (;;)
1571     {
1572 	if (!*ptr)
1573 	{
1574 	    enumError err = XCreateSplitFile( XCALL f, ptr-f->split_f );
1575 	    if (err)
1576 		return err;
1577 	}
1578 
1579 	File_t *cur = *ptr;
1580 	ASSERT(cur);
1581 	if ( off <= cur->split_filesize )
1582 	{
1583 	    *p_index = ptr - f->split_f;
1584 	    *p_off = off;
1585 	    return ERR_OK;
1586 	}
1587 
1588 	// skip this file
1589 	off -= cur->split_filesize;
1590 	ptr++;
1591     }
1592 }
1593 
1594 //
1595 ///////////////////////////////////////////////////////////////////////////////
1596 ///////////////////////////////////////////////////////////////////////////////
1597 
ClearCache(File_t * f)1598 void ClearCache	( File_t * f )
1599 {
1600     DASSERT(f);
1601     f->is_caching = false;
1602     while (f->cache)
1603     {
1604 	FileCache_t * ptr = f->cache;
1605 	f->cache = ptr->next;
1606 	FREE((char*)ptr->data);
1607 	FREE(ptr);
1608     }
1609     f->cur_cache = 0;
1610 }
1611 
1612 ///////////////////////////////////////////////////////////////////////////////
1613 
DefineCachedArea(File_t * f,off_t off,size_t count)1614 void DefineCachedArea ( File_t * f, off_t off, size_t count )
1615 {
1616     // this whole function assumes that count is small and that the hole
1617     // cache size is less than MAX(size_t)
1618 
1619     ASSERT(f);
1620 
1621     f->cur_cache = 0;
1622     f->is_caching = f->fd != -1 && f->is_reading && !f->is_writing;
1623     TRACE("DefineCachedArea(,%llx,%zx) fd=%d rw=%d,%d is_caching=%d, filesize=%llx\n",
1624 		(u64)off, count, f->fd, f->is_reading, f->is_writing, f->is_caching,
1625 		(u64)f->st.st_size );
1626     if (!f->is_caching)
1627 	return;
1628 
1629     const size_t blocksize = f->st.st_blksize > HD_SECTOR_SIZE
1630 			   ? f->st.st_blksize : HD_SECTOR_SIZE;
1631 
1632     off_t off_end = (( off + count + blocksize - 1 ) / blocksize ) * blocksize;
1633     off = ( off / blocksize ) * blocksize;
1634     count = (size_t)( off_end - off );
1635 
1636     if (f->st.st_size)
1637     {
1638 	// limit cache to file size if filesize is known
1639 
1640 	if ( off >= f->st.st_size )
1641 	    return;
1642 
1643 	if ( off + count > f->st.st_size )
1644 	    count = f->st.st_size - off;
1645     }
1646 
1647     FileCache_t **pptr;
1648     for ( pptr = &f->cache; *pptr; pptr = &(*pptr)->next )
1649     {
1650 	FileCache_t * ptr = *pptr;
1651 	if ( off_end < ptr->off )
1652 	    break;
1653 
1654 	off_t ptr_end = ptr->off + ptr->count;
1655 	if ( off < ptr_end || off == ptr_end && !ptr->data )
1656 	{
1657 	    // extend this cache element
1658 	    if (ptr->data)
1659 	    {
1660 		// already cached -> try a smaller area
1661 		if ( off_end > ptr_end )
1662 		    DefineCachedArea(f,ptr_end,off_end-ptr_end);
1663 		return;
1664 	    }
1665 
1666 	    off_t new_end = ptr->off + ptr->count;
1667 	    if ( new_end < off_end )
1668 		new_end = off_end;
1669 
1670 	    if ( ptr->off < off )
1671 		 ptr->off = off;
1672 	    ptr->count = new_end - ptr->off;
1673 	    TRACE("#F# CACHE ENTRY extended: o=%10llx n=%9zx\n",(u64)ptr->off,ptr->count);
1674 	    return;
1675 	}
1676     }
1677 
1678     // insert a new cache element
1679     FileCache_t * ptr = CALLOC(1,sizeof(FileCache_t));
1680     ptr->off   = off;
1681     ptr->count = count;
1682     ptr->next  = *pptr;
1683     *pptr = ptr;
1684     TRACE("#F# CACHE ENTRY inserted: o=%10llx n=%9zx\n",(u64)ptr->off,ptr->count);
1685 }
1686 
1687 //-----------------------------------------------------------------------------
1688 
DefineCachedAreaISO(File_t * f,bool head_only)1689 void DefineCachedAreaISO ( File_t * f, bool head_only )
1690 {
1691     ASSERT(f);
1692     if (head_only)
1693     {
1694 	DefineCachedArea(f,0,CISO_HEAD_SIZE+FILE_PRELOAD_SIZE);
1695     }
1696     else
1697     {
1698 	DefineCachedArea(f,0x0000000ull,0x200000);
1699 	DefineCachedArea(f,0xf800000ull,0x800000);
1700     }
1701 }
1702 
1703 ///////////////////////////////////////////////////////////////////////////////
1704 ///////////////////////////////////////////////////////////////////////////////
1705 
XAnalyzeWH(XPARM File_t * f,WDF_Head_t * wh,bool print_err)1706 enumError XAnalyzeWH ( XPARM File_t * f, WDF_Head_t * wh, bool print_err )
1707 {
1708     ASSERT(wh);
1709     TRACE("AnalyzeWH()\n");
1710 
1711     if (memcmp(wh->magic,WDF_MAGIC,WDF_MAGIC_SIZE))
1712     {
1713 	TRACE(" - magic failed\n");
1714 	return ERR_NO_WDF;
1715     }
1716 
1717     if (!f->seek_allowed)
1718     {
1719 	TRACE(" - seek not allowed\n");
1720 	return print_err
1721 		? PrintError( XERROR0, ERR_WRONG_FILE_TYPE,
1722 			"Wrong file type: %s\n", f->fname )
1723 		: ERR_WRONG_FILE_TYPE;
1724     }
1725 
1726     const size_t wdf_head_size = AdjustHeaderWDF(wh);
1727 
1728  #if WDF2_ENABLED
1729     if ( !wh->wdf_version || wh->wdf_compatible > WDF_VERSION )
1730  #else
1731     if ( !wh->wdf_version || wh->wdf_version > WDF_VERSION )
1732  #endif
1733     {
1734      #if WDF2_ENABLED
1735 	PRINT(" - wrong WDF version: %u,%u\n",wh->wdf_version,wh->wdf_compatible);
1736      #else
1737 	PRINT(" - wrong WDF version: %u\n",wh->wdf_version);
1738      #endif
1739 	return print_err
1740 		? PrintError( XERROR0, ERR_WDF_VERSION,
1741  #if WDF2_ENABLED
1742 			"Only WDF version 1..%u supported but not version %u.\n",
1743  #else
1744 			"Only WDF version %u supported but not version %u.\n",
1745  #endif
1746 			WDF_VERSION, wh->wdf_version )
1747 		: ERR_WDF_VERSION;
1748     }
1749 
1750     const u32 chunk_tab_size = wh->chunk_n * sizeof(WDF_Chunk_t);
1751     if ( f->st.st_size < wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size )
1752     {
1753 	// file size to short -> maybe a splitted file
1754 	XSetupSplitFile(XCALL f,OFT_UNKNOWN,0);
1755     }
1756 
1757     if ( wh->chunk_off != wh->data_size + wdf_head_size
1758 	|| wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size != f->st.st_size )
1759     {
1760 	PRINT(" - file size error\n");
1761 	PRINT("   - %llx ? %llx = %llx + %zx\n",
1762 		(u64)wh->chunk_off, wh->data_size + wdf_head_size,
1763 		wh->data_size, wdf_head_size );
1764 	PRINT("   - %llx + %x + %x = %llx ? %llx\n",
1765 		(u64)wh->chunk_off, WDF_MAGIC_SIZE, chunk_tab_size,
1766 		(u64)wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size,
1767 		(u64)f->st.st_size );
1768 
1769 	return print_err
1770 		? PrintError( XERROR0, ERR_WDF_INVALID, "Invalid WDF file: %s\n",f->fname )
1771 		: ERR_WDF_INVALID;
1772     }
1773 
1774     TRACE(" - OK\n");
1775     return ERR_OK;
1776 }
1777 
1778 //
1779 ///////////////////////////////////////////////////////////////////////////////
1780 ///////////////////////////////////////////////////////////////////////////////
1781 
XCacheHelper(XPARM File_t * f,off_t off,size_t count)1782 static FileCache_t * XCacheHelper ( XPARM File_t * f, off_t off, size_t count )
1783 {
1784     // This function does the following:
1785     // a) look into the cache table to find if (off,count) is part of an
1786     //    cache entry. I so, the result is a pointer to the first affected
1787     //    cache entry.
1788     // b) All cache entires up to off are filled with data. If 'off' is part
1789     //    of an entry this entry is filled completly.
1790     // c) If the result is not NULL a seek or a seek simulation is done.
1791 
1792     ASSERT(f);
1793     f->last_error = ERR_OK;
1794     if (!f->is_caching)
1795 	return 0;
1796 
1797     // cur_cache is always pointing to the last used and loaded cache entry
1798     ASSERT( !f->cur_cache || f->cur_cache->data );
1799 
1800     FileCache_t * cptr = f->cur_cache;
1801     if ( !cptr || off < cptr->off )
1802 	cptr = f->cache;
1803 
1804     f->is_caching = false; // disable cache access for sub calls
1805     while (cptr)
1806     {
1807 	TRACE(" - SEARCH=%llx..%llx  CACHE=%llx..%llx\n",
1808 	    (u64)off, (u64)off + count, (u64)cptr->off, (u64)cptr->off + cptr->count );
1809 
1810 	if ( off + count <= cptr->off )
1811 	{
1812 	    cptr = 0;
1813 	    break;
1814 	}
1815 
1816 	if (!cptr->data)
1817 	{
1818 	    // cache must be filled
1819 	    char * data = MALLOC(cptr->count);
1820 	    cptr->data = data;
1821 	    TRACE(TRACE_RDWR_FORMAT, "#F# FILL CACHE",
1822 		GetFD(f), GetFP(f), (u64)cptr->off,
1823 		(u64)cptr->off+cptr->count, cptr->count, "" );
1824 	    const enumError stat = XReadAtF(XCALL f,cptr->off,data,cptr->count);
1825 	    if (stat)
1826 	    {
1827 		cptr = 0;
1828 		break;
1829 	    }
1830 	}
1831 	if ( off < cptr->off + cptr->count )
1832 	    break;
1833 	cptr = cptr->next;
1834     }
1835     f->is_caching = true; // restore cache access
1836 
1837     if (cptr)
1838     {
1839 	// cache entry found!
1840 	ASSERT( off + count > cptr->off );
1841 	ASSERT( off < cptr->off + cptr->count );
1842 
1843 	f->cur_off = off;
1844 	f->cur_cache = cptr; // save the current ptr
1845 	ASSERT(cptr->data);
1846 
1847 	TRACE(TRACE_RDWR_FORMAT, "#F# CACHE FOUND",
1848 		GetFD(f), GetFP(f), (u64)cptr->off,
1849 		(u64)cptr->off+cptr->count, cptr->count, "" );
1850     }
1851 
1852     return cptr;
1853 }
1854 
1855 ///////////////////////////////////////////////////////////////////////////////
1856 
PreallocHelper(File_t * f)1857 static void PreallocHelper ( File_t *f )
1858 {
1859     DASSERT(f);
1860 
1861  #ifndef NO_PREALLOC
1862     if ( !f->prealloc_done && f->prealloc_map.used )
1863     {
1864 	f->prealloc_done = true;
1865 	if (f->split_f)
1866 	{
1867 	    ExtractSplitMap(f,f->split_f[0]);
1868 	    PreallocHelper(f->split_f[0]);
1869 	}
1870 	else
1871 	{
1872 	    if ( logging > 0 )
1873 	    {
1874 		printf("\n Preallocation table:\n");
1875 		PrintMemMap(&f->prealloc_map,stdout,3,0);
1876 		putchar('\n');
1877 	    }
1878 
1879 	    // prealloc largest block first
1880 
1881 	    MemMapItem_t ** end_field = f->prealloc_map.field + f->prealloc_map.used;
1882 	    for(;;)
1883 	    {
1884 		off_t found_size = 0;
1885 		MemMapItem_t **ptr, *found = 0;
1886 		for ( ptr = f->prealloc_map.field; ptr < end_field; ptr++ )
1887 		    if ( (*ptr)->size > found_size )
1888 		    {
1889 			found = *ptr;
1890 			found_size = found->size;
1891 		    }
1892 		if (!found)
1893 		    break;
1894 
1895 	#ifdef HAVE_FALLOCATE
1896 		noPRINT("CALL fallocate(%d,0,%9llx,%9llx [%s])\n",
1897 			    f->fd, (u64)found->off, (u64)found->size,
1898 			    wd_print_size_1024(0,0,found->size,true) );
1899 		fallocate(f->fd,0,found->off,found->size);
1900 		noPRINT("TERM fallocate()\n");
1901 	#elif HAVE_POSIX_FALLOCATE
1902 		noPRINT("CALL posix_fallocate(%d,%9llx,%9llx [%s])\n",
1903 			    f->fd, (u64)found->off, (u64)found->size,
1904 			    wd_print_size_1024(0,0,found->size,true) );
1905 		posix_fallocate(f->fd,found->off,found->size);
1906 		noPRINT("TERM posix_fallocate()\n");
1907 	#elif __APPLE__
1908 		noPRINT("CALL fcntl(%d,F_PREALLOCATE,%9llx,%9llx [%s])\n",
1909 			    f->fd, (u64)found->off, (u64)found->size,
1910 			    wd_print_size_1024(0,0,found->size,true) );
1911 		fstore_t fst;
1912 		fst.fst_flags	= F_ALLOCATECONTIG;
1913 		fst.fst_posmode	= F_PEOFPOSMODE;
1914 		fst.fst_offset	= found->off;
1915 		fst.fst_length	= found->size;
1916 		fst.fst_bytesalloc = 0;
1917 		fcntl( f->fd, F_PREALLOCATE, &fst );
1918 		noPRINT("TERM fcntl()\n");
1919 	#else
1920 		#error "no preallocation support -> use -DNO_PREALLOC"
1921 	#endif
1922 
1923 		found->size = 0;
1924 	    }
1925 	}
1926     }
1927  #endif
1928 }
1929 
1930 ///////////////////////////////////////////////////////////////////////////////
1931 
XTellF(XPARM File_t * f)1932 enumError XTellF ( XPARM File_t * f )
1933 {
1934     ASSERT(f);
1935 
1936     if (f->split_f)
1937     {
1938 	// file pos is pure virtual
1939 	return f->cur_off;
1940     }
1941 
1942     f->file_off = f->fp
1943 		    ? ftello(f->fp)
1944 		    : f->fd != -1
1945 			? lseek(f->fd,(off_t)0,SEEK_CUR)
1946 			: (off_t)-1;
1947 
1948     if ( f->file_off == (off_t)-1 )
1949     {
1950 	f->last_error = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
1951 	if ( f->max_error < f->last_error )
1952 	    f->max_error = f->last_error;
1953 	if (!f->disable_errors)
1954 	    PrintError( XERROR1, f->last_error, "Tell failed [%c=%d]: %s\n",
1955 			GetFT(f), GetFD(f), f->fname );
1956     }
1957     else
1958     {
1959 	f->last_error = ERR_OK;
1960 	f->tell_count++;
1961 	if ( f->max_off < f->file_off )
1962 	    f->max_off = f->file_off;
1963     }
1964 
1965     TRACE(TRACE_SEEK_FORMAT, "#F# TellF()", GetFD(f), GetFP(f), (u64)f->file_off, "" );
1966     f->cur_off = f->file_off;
1967     return f->last_error;
1968 }
1969 
1970 ///////////////////////////////////////////////////////////////////////////////
1971 
XSeekF(XPARM File_t * f,off_t off)1972 enumError XSeekF ( XPARM File_t * f, off_t off )
1973 {
1974     ASSERT(f);
1975 
1976     // sync virtual off
1977     f->cur_off = f->file_off;
1978 
1979     // if we are already at off and pure reading or pure writing -> all done
1980     if ( off == f->file_off && f->is_writing != f->is_reading )
1981 	return f->last_error = ERR_OK;
1982 
1983     if (f->is_caching)
1984     {
1985 	FileCache_t * cptr = XCacheHelper(XCALL f,off,1);
1986 	if (cptr)
1987 	    return f->last_error; // all done
1988     }
1989 
1990     if (!f->prealloc_done)
1991 	PreallocHelper(f);
1992 
1993     if (f->split_f)
1994     {
1995 	// file pos is pure virtual
1996 	f->cur_off = off;
1997 	return ERR_OK;
1998     }
1999 
2000     if (!f->seek_allowed)
2001     {
2002 	if ( !f->is_reading && off >= f->file_off )
2003 	{
2004 	    // simulate seek with 'write zero' operations
2005 
2006 	    off_t skip_size = off - f->file_off;
2007 	    TRACE(TRACE_RDWR_FORMAT, "#F# SEEK SIMULATION",
2008 			    GetFD(f), GetFP(f), (u64)f->file_off,
2009 			    (u64)f->file_off+skip_size, (size_t)skip_size, "" );
2010 
2011 	    while (skip_size)
2012 	    {
2013 		if (SIGINT_level>1)
2014 		    return ERR_INTERRUPT;
2015 
2016 		const size_t max_write = skip_size < sizeof(zerobuf)
2017 				      ? (size_t)skip_size : sizeof(zerobuf);
2018 		const enumError stat = XWriteF(XCALL f,zerobuf,max_write);
2019 		if (stat)
2020 		    return stat;
2021 		skip_size -= max_write;
2022 		noTRACE("R+W-Count: %llu + %llu\n",f->bytes_read,f->bytes_written);
2023 	    }
2024 	    ASSERT( SIGINT_level || off == f->file_off );
2025 	    ASSERT( SIGINT_level || off == f->cur_off );
2026 	    return ERR_OK;
2027 	}
2028 
2029 	if ( !f->is_writing && off >= f->file_off )
2030 	{
2031 	    // simulate seek with read operations
2032 
2033 	    off_t skip_size = off - f->file_off;
2034 	    TRACE(TRACE_RDWR_FORMAT, "#F# SEEK SIMULATION",
2035 			    GetFD(f), GetFP(f), (u64)f->file_off,
2036 			    (u64)f->file_off+skip_size, (size_t)skip_size, "" );
2037 
2038 	    while (skip_size)
2039 	    {
2040 		if (SIGINT_level>1)
2041 		    return ERR_INTERRUPT;
2042 
2043 		const size_t max_read = skip_size < sizeof(iobuf)
2044 				      ? (size_t)skip_size : sizeof(iobuf);
2045 		const enumError stat = XReadF(XCALL f,iobuf,max_read);
2046 		if (stat)
2047 		    return stat;
2048 		skip_size -= max_read;
2049 		noTRACE("R+W-Count: %llu + %llu\n",f->bytes_read,f->bytes_written);
2050 	    }
2051 	    ASSERT( SIGINT_level || off == f->file_off );
2052 	    ASSERT( SIGINT_level || off == f->cur_off );
2053 	    return ERR_OK;
2054 	}
2055 
2056 	if ( f->is_caching )
2057 	{
2058 	    char buf[100];
2059 	    snprintf(buf,sizeof(buf),"ID=%s, OFF=%llx, SIZE=%x",
2060 		    f->id6_src, (u64)f->cache_info_off, (u32)f->cache_info_size );
2061 	    fprintf(stderr,
2062 		    "\n"
2063 		    "************************************************************************\n"
2064 		    "*****  It seems, that the caching area for the game is too small!  *****\n"
2065 		    "*****  Please report this to the author.                           *****\n"
2066 		    "*****  Technical data: %-43s *****\n"
2067 		    "************************************************************************\n"
2068 		    "\n", buf );
2069 	}
2070     }
2071 
2072     TRACE(TRACE_SEEK_FORMAT, "#F# SeekF()",
2073 		GetFD(f), GetFP(f), (u64)off, off < f->max_off ? " <" : "" );
2074 
2075     const bool failed = f->fp
2076 			? fseeko(f->fp,off,SEEK_SET) == (off_t)-1
2077 			: f->fd == -1 || lseek(f->fd,off,SEEK_SET) == (off_t)-1;
2078 
2079     enumError err;
2080     if (failed)
2081     {
2082 	err = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
2083 	f->last_error = err;
2084 	if ( f->max_error < f->last_error )
2085 	    f->max_error = f->last_error;
2086 	if (!f->disable_errors)
2087 	    PrintError( XERROR1, f->last_error,
2088 			"Seek failed [%c=%d,%llu]: %s\n",
2089 			GetFT(f), GetFD(f), (u64)off, f->fname );
2090 	f->file_off = (off_t)-1;
2091     }
2092     else
2093     {
2094 	err = ERR_OK;
2095 	f->seek_count++;
2096 	if ( f->max_off < f->file_off )
2097 	    f->max_off = f->file_off;
2098     }
2099 
2100     f->cur_off = f->file_off = off;
2101     return err;
2102 }
2103 
2104 ///////////////////////////////////////////////////////////////////////////////
2105 
XSetSizeF(XPARM File_t * f,off_t size)2106 enumError XSetSizeF ( XPARM File_t * f, off_t size )
2107 {
2108     ASSERT(f);
2109     TRACE(TRACE_SEEK_FORMAT, "#F# SetSizeF()",
2110 		GetFD(f), GetFP(f), (u64)size,
2111 		size < f->max_off ? " <" : size > f->max_off ? " >" : "" );
2112 
2113     if (!f->prealloc_done)
2114 	PreallocHelper(f);
2115 
2116     if (f->split_f)
2117     {
2118 	f->max_off = size;
2119 
2120 	uint index;
2121 	enumError err = XFindSplitFile( XCALL f, &index, &size );
2122 	if (err)
2123 	    return err;
2124 	ASSERT( index < MAX_SPLIT_FILES );
2125 	File_t ** ptr = f->split_f + index;
2126 	XSetSizeF(XCALL *ptr,size);
2127 
2128 	int count = f->split_used - index;
2129 	f->split_used = index+1;
2130 	while ( count-- > 1 )
2131 	{
2132 	    ptr++;
2133 	    ASSERT(*ptr);
2134 	    XCloseFile( XCALL *ptr, true );
2135 	    FREE(*ptr);
2136 	    *ptr = 0;
2137 	}
2138 	return ERR_OK;
2139     }
2140 
2141     //--------------------------------------------------
2142 
2143     if (f->fp)
2144 	fflush(f->fp); // [[2do]] ? error handling
2145 
2146     if ( !f->seek_allowed && f->cur_off <= size )
2147     {
2148 	if (!XSeekF(XCALL f,size))
2149 	    return f->last_error;
2150     }
2151     else if (ftruncate(f->fd,size))
2152     {
2153 	f->last_error = ERR_WRITE_FAILED;
2154 	if ( f->max_error < f->last_error )
2155 	    f->max_error = f->last_error;
2156 	if (!f->disable_errors)
2157 	    PrintError( XERROR1, f->last_error,
2158 			"Set file size failed [%c=%d,%llu]: %s\n",
2159 			GetFT(f), GetFD(f), (u64)size, f->fname );
2160 	return f->last_error;
2161     }
2162 
2163     PRINT("fd=%d truncated to %llx\n",f->fd,(u64)size);
2164     f->setsize_count++;
2165     f->max_off = size;
2166     return ERR_OK;
2167 }
2168 
2169 ///////////////////////////////////////////////////////////////////////////////
2170 
XPreallocateF(XPARM File_t * f,off_t off,off_t size)2171 enumError XPreallocateF
2172 (
2173     XPARM			// debugging and tracing infos
2174     File_t	* f,		// valid file, writing
2175     off_t	off,		// offset
2176     off_t	size		// needed size
2177 )
2178 {
2179     DASSERT(f);
2180     enumError err = ERR_OK;
2181     if ( size && prealloc_mode > PREALLOC_OFF )
2182     {
2183 	noPRINT("PREALLOC/FILE fd=%d, %llx+%llx max=%llx: %s\n",
2184 		f->fd, (u64)off, (u64)size, (u64)f->max_off, f->fname );
2185 
2186 	const off_t max = off + size;
2187 	if ( f->prealloc_size < max )
2188 	     f->prealloc_size = max;
2189 
2190 	InsertMemMapTie(&f->prealloc_map,off,size);
2191     }
2192     return err;
2193 }
2194 
2195 ///////////////////////////////////////////////////////////////////////////////
2196 
XReadF(XPARM File_t * f,void * iobuf,size_t count)2197 enumError XReadF ( XPARM File_t * f, void * iobuf, size_t count )
2198 {
2199     ASSERT(f);
2200 
2201     if (f->is_caching)
2202     {
2203 	TRACE("#F# --- cache\n");
2204 	TRACE(TRACE_RDWR_FORMAT, "#F# ReadF()+C",
2205 		GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2206 		f->cur_off < f->max_off ? " <" : "" );
2207 
2208 	off_t my_off = f->cur_off;
2209 	while (count)
2210 	{
2211 	    FileCache_t * cptr = XCacheHelper(XCALL f,my_off,count);
2212 	    if (f->last_error)
2213 		return f->last_error;
2214 	    if (!cptr)
2215 		break;
2216 
2217 	    // there is an overlap
2218 	    TRACE("   my_off=%llx, count=%zx, cptr->off=%llx, cptr->count=%zx\n",
2219 			(u64)my_off, count, (u64)cptr->off, cptr->count );
2220 	    //ASSERT( my_off + count > cptr->off );
2221 	    ASSERT( cptr->off + cptr->count > my_off );
2222 
2223 	    if ( cptr->off > my_off )
2224 	    {
2225 		// read data from file
2226 
2227 		TRACELINE;
2228 		const size_t size = cptr->off - my_off;
2229 		f->is_caching = false; // disable cache operations
2230 		const enumError stat = XReadAtF(XCALL f,my_off,iobuf,size);
2231 		f->is_caching = true; // restore cache operations
2232 		if (stat)
2233 		    return stat;
2234 		iobuf = (void*)( (char*)iobuf + size );
2235 		count -= size;
2236 		my_off += size;
2237 	    }
2238 
2239 	    if ( count > 0 )
2240 	    {
2241 		TRACELINE;
2242 		ASSERT( my_off >= cptr->off );
2243 		ASSERT( my_off <  cptr->off + cptr->count );
2244 		ASSERT( cptr->data );
2245 		const u32 delta = my_off - cptr->off;
2246 		ASSERT ( delta < cptr->count );
2247 		size_t size = cptr->count - delta;
2248 		if ( size > count )
2249 		    size = count;
2250 		TRACE(TRACE_RDWR_FORMAT, "#F# COPY FROM CACHE",
2251 			GetFD(f), GetFP(f), (u64)my_off, (u64)my_off+size, size, "" );
2252 		memcpy(iobuf,cptr->data+delta,size);
2253 		iobuf = (void*)( (char*)iobuf + size );
2254 		count -= size;
2255 		my_off += size;
2256 		f->bytes_read += size;
2257 	    }
2258 
2259 	    f->cur_off = my_off;
2260 	    if (!count)
2261 		return ERR_OK;
2262 	}
2263     }
2264 
2265     //--------------------------------------------------
2266 
2267     if (f->split_f)
2268     {
2269 	TRACE("#S# ---\n");
2270 	TRACE(TRACE_RDWR_FORMAT, "#S#   ReadF()",
2271 		GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2272 		f->cur_off < f->max_off ? " <" : "" );
2273 
2274 	File_t ** ptr = f->split_f;
2275 	off_t off = f->cur_off;
2276 	f->cur_off = 0;
2277 
2278 	while ( count > 0 )
2279 	{
2280 	    if (!*ptr)
2281 	    {
2282 		ptr--;
2283 		f->cur_off += off + count;
2284 		f->bytes_read += count;
2285 		return XReadAtF( XCALL *ptr, off, iobuf, count );
2286 	    }
2287 
2288 	    File_t *cur = *ptr;
2289 	    ASSERT(cur);
2290 	    TRACE("#S#%zd# off=%llx cur_of=%llx count=%zx fsize=%llx\n",
2291 			ptr-f->split_f, (u64)off, (u64)f->cur_off, count, (u64)cur->split_filesize );
2292 
2293 	    if ( off < cur->split_filesize )
2294 	    {
2295 		// read from this file
2296 		const off_t max_count = cur->split_filesize - off;
2297 		const size_t stream_count = count < max_count ? count : (size_t)max_count;
2298 		enumError err = XReadAtF( XCALL cur, off, iobuf, stream_count );
2299 		if (err)
2300 		    return err;
2301 		f->bytes_read += stream_count;
2302 		count -= stream_count;
2303 		iobuf = (char*)iobuf + stream_count;
2304 		f->cur_off += off + stream_count;
2305 		off = 0;
2306 	    }
2307 	    else
2308 	    {
2309 		// skip this file
2310 		f->cur_off += cur->split_filesize;
2311 		off -= cur->split_filesize;
2312 	    }
2313 	    ptr++;
2314 	}
2315 	if ( f->max_off < f->cur_off )
2316 	    f->max_off = f->cur_off;
2317 
2318 	TRACE("#S#x# off=%9llx cur_of=%9llx count=%zx\n", (u64)off, (u64)f->cur_off, count );
2319 	return ERR_OK;
2320     }
2321 
2322     //--------------------------------------------------
2323 
2324     if ( f->cur_off != f->file_off )
2325     {
2326 	const int stat = XSeekF(XCALL f,f->cur_off);
2327 	if (stat)
2328 	    return stat;
2329 	// f->cur_off and f->file_off may differ because of cache access
2330     }
2331 
2332     TRACE(TRACE_RDWR_FORMAT, "#F# ReadF()",
2333 		GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2334 		f->cur_off < f->max_off ? " <" : "" );
2335 
2336     if ( f->read_behind_eof && ( f->st.st_size > 0 || f->is_writing ) )
2337     {
2338 	const off_t max_read = f->st.st_size > f->file_off
2339 					? f->st.st_size - f->file_off
2340 					: 0;
2341 
2342 	TRACE("read_behind_eof=%d, st_size=%llx, count=%zx, max_read=%llx\n",
2343 		f->read_behind_eof, (u64)f->st.st_size, count, (u64)max_read );
2344 
2345 	if ( count > max_read )
2346 	{
2347 	    if ( f->read_behind_eof == 1 )
2348 	    {
2349 		f->read_behind_eof = 2;
2350 		if ( !f->disable_errors )
2351 		    PrintError( XERROR0, ERR_WARNING,
2352 			"Read behind eof -> zero filled [%c=%d,%llu+%zu]: %s\n",
2353 			GetFT(f), GetFD(f),
2354 			(u64)f->file_off, count, f->fname );
2355 	    }
2356 	    size_t fill_count = count - (size_t)max_read;
2357 	    count = (size_t)max_read;
2358 	    memset(iobuf+count,0,fill_count);
2359 
2360 	    if (!count)
2361 		return ERR_OK;
2362 	}
2363     }
2364 
2365     bool err;
2366     size_t read_count = 0;
2367     if ( f->fd == -1 )
2368     {
2369 	err = true;
2370 	errno = 0;
2371     }
2372     else if (!count)
2373 	err = false;
2374     else if (f->fp)
2375     {
2376 	read_count = fread(iobuf,1,count,f->fp);
2377 	err = read_count < count && errno;
2378 	iobuf = (void*)( (char*)iobuf + read_count );
2379     }
2380     else
2381     {
2382 	err = false;
2383 	size_t size = count;
2384 	while (size)
2385 	{
2386 	    ssize_t rstat = read(f->fd,iobuf,size);
2387 	    if ( rstat <= 0 )
2388 	    {
2389 		err = rstat < 0;
2390 		break;
2391 	    }
2392 	    read_count += rstat;
2393 	    size -= rstat;
2394 	    iobuf = (void*)( (char*)iobuf + rstat );
2395 	}
2396     }
2397 
2398     if ( err || read_count < count && !f->read_behind_eof )
2399     {
2400 	if ( !f->disable_errors && f->last_error != ERR_READ_FAILED )
2401 	    PrintError( XERROR1, ERR_READ_FAILED,
2402 			"Read failed [%c=%d,%llu+%zu]: %s\n",
2403 			GetFT(f), GetFD(f),
2404 			(u64)f->file_off, count, f->fname );
2405 
2406 	f->cur_off = f->file_off = (off_t)-1ll;
2407 
2408 	if ( f->max_error < ERR_READ_FAILED )
2409 	     f->max_error = ERR_READ_FAILED;
2410 	return f->last_error = ERR_READ_FAILED;
2411     }
2412 
2413     f->read_count++;
2414     f->bytes_read += read_count;
2415     f->file_off += read_count;
2416     f->cur_off = f->file_off;
2417     if ( f->max_off < f->file_off )
2418 	f->max_off = f->file_off;
2419 
2420     if ( read_count < count )
2421     {
2422 	if (!f->st.st_size)
2423 	    f->st.st_size = f->file_off;
2424 	f->cur_off = f->file_off;
2425 	return XReadAtF(XCALL f,f->file_off,iobuf,count-read_count);
2426     }
2427 
2428     return ERR_OK;
2429 }
2430 
2431 ///////////////////////////////////////////////////////////////////////////////
2432 
XWriteF(XPARM File_t * f,const void * iobuf,size_t count)2433 enumError XWriteF ( XPARM File_t * f, const void * iobuf, size_t count )
2434 {
2435     ASSERT(f);
2436 
2437     if (!f->prealloc_done)
2438 	PreallocHelper(f);
2439 
2440     if (f->split_f)
2441     {
2442 	TRACE("#S# ---\n");
2443 	TRACE(TRACE_RDWR_FORMAT, "#S#   WriteF()",
2444 		GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2445 		f->cur_off < f->max_off ? " <" : "" );
2446 
2447 	File_t ** ptr = f->split_f;
2448 	off_t off = f->cur_off;
2449 	f->cur_off = 0;
2450 
2451 	while ( count > 0 )
2452 	{
2453 	    if (!*ptr)
2454 	    {
2455 		enumError err = XCreateSplitFile( XCALL f, ptr-f->split_f );
2456 		if (err)
2457 		    return err;
2458 	    }
2459 
2460 	    File_t *cur = *ptr;
2461 	    ASSERT(cur);
2462 	    TRACE("#S#%zd# off=%llx cur_of=%llx count=%zx fsize=%llx\n",
2463 			ptr-f->split_f, (u64)off, (u64)f->cur_off,
2464 			count, (u64)cur->split_filesize );
2465 
2466 	    if ( off < cur->split_filesize )
2467 	    {
2468 		// write to this file
2469 		const off_t max_count = cur->split_filesize - off;
2470 		const size_t stream_count = count < max_count ? count : (size_t)max_count;
2471 		enumError err = XWriteAtF( XCALL cur, off, iobuf, stream_count );
2472 		if (err)
2473 		    return err;
2474 		f->bytes_written += stream_count;
2475 		count -= stream_count;
2476 		iobuf = (char*)iobuf + stream_count;
2477 		f->cur_off += off + stream_count;
2478 		off = 0;
2479 	    }
2480 	    else
2481 	    {
2482 		// skip this file
2483 		f->cur_off += cur->split_filesize;
2484 		off -= cur->split_filesize;
2485 	    }
2486 	    ptr++;
2487 	}
2488 	if ( f->max_off < f->cur_off )
2489 	    f->max_off = f->cur_off;
2490 
2491 	TRACE("#S#x# off=%9llx cur_of=%9llx count=%zx\n", (u64)off, (u64)f->cur_off, count );
2492 	return ERR_OK;
2493     }
2494 
2495     //--------------------------------------------------
2496 
2497     ASSERT( f->cur_off == f->file_off ); // no cache while writing
2498     TRACE(TRACE_RDWR_FORMAT, "#F# WriteF()",
2499 		GetFD(f), GetFP(f), (u64)f->file_off, (u64)f->file_off+count, count,
2500 		f->file_off < f->max_off ? " <" : "" );
2501 
2502     bool err;
2503     if (f->fp)
2504 	err = count && fwrite(iobuf,count,1,f->fp) != 1;
2505     else if ( f->fd != -1 )
2506     {
2507 	err = false;
2508 	size_t size = count;
2509 	while (size)
2510 	{
2511 	    ssize_t wstat = write(f->fd,iobuf,size);
2512 	    if ( wstat <= 0 )
2513 	    {
2514 		err = true;
2515 		break;
2516 	    }
2517 	    size -= wstat;
2518 	    iobuf = (void*)( (char*)iobuf + wstat );
2519 	}
2520     }
2521     else
2522     {
2523 	err = true;
2524 	errno = 0;
2525     }
2526 
2527     if (err)
2528     {
2529 	if ( !f->disable_errors && f->last_error != ERR_WRITE_FAILED )
2530 	    PrintError( XERROR1, ERR_WRITE_FAILED,
2531 				"Write failed [%c=%d,%llu+%zu]: %s\n",
2532 				GetFT(f), GetFD(f),
2533 				(u64)f->file_off, count, f->fname );
2534 
2535 	f->cur_off = f->file_off = (off_t)-1ll;
2536 
2537 	if ( f->max_error < ERR_WRITE_FAILED )
2538 	     f->max_error = ERR_WRITE_FAILED;
2539 	return f->last_error = ERR_WRITE_FAILED;
2540     }
2541 
2542     f->write_count++;
2543     f->bytes_written += count;
2544     f->file_off += count;
2545     if ( f->max_off < f->file_off )
2546 	f->max_off = f->file_off;
2547     f->cur_off = f->file_off;
2548     return ERR_OK;
2549 }
2550 
2551 ///////////////////////////////////////////////////////////////////////////////
2552 
XReadAtF(XPARM File_t * f,off_t off,void * iobuf,size_t count)2553 enumError XReadAtF ( XPARM File_t * f, off_t off, void * iobuf, size_t count )
2554 {
2555     ASSERT(f);
2556     noTRACE("#F# ReadAtF(fd=%d,o=%llx,%p,n=%zx)\n",f->fd,(u64)off,iobuf,count);
2557     f->cache_info_off  = off;
2558     f->cache_info_size = count;
2559     const enumError stat = XSeekF(XCALL f,off);
2560     return stat ? stat : XReadF(XCALL f,iobuf,count);
2561 }
2562 
2563 ///////////////////////////////////////////////////////////////////////////////
2564 
XWriteAtF(XPARM File_t * f,off_t off,const void * iobuf,size_t count)2565 enumError XWriteAtF ( XPARM File_t * f, off_t off, const void * iobuf, size_t count )
2566 {
2567     ASSERT(f);
2568     noTRACE("#F# WriteAtF(fd=%d,o=%llx,%p,n=%zx)\n",f->fd,(u64)off,iobuf,count);
2569     const enumError stat = XSeekF(XCALL f,off);
2570     return stat ? stat : XWriteF(XCALL f,iobuf,count);
2571 }
2572 
2573 ///////////////////////////////////////////////////////////////////////////////
2574 
XWriteZeroAtF(XPARM File_t * f,off_t off,size_t count)2575 enumError XWriteZeroAtF ( XPARM File_t * f, off_t off, size_t count )
2576 {
2577     TRACE(TRACE_RDWR_FORMAT, "#F# WriteZeroAtF()",
2578 		GetFD(f), GetFP(f), (u64)off, (u64)off+count, count,
2579 		off < f->max_off ? " <" : "" );
2580 
2581     enumError err = XSeekF(XCALL f,off);
2582     while ( !err && count > 0 )
2583     {
2584 	const size_t wsize = count < sizeof(zerobuf) ? count : sizeof(zerobuf);
2585 	err = XWriteF(XCALL f, zerobuf, wsize );
2586 	count -= wsize;
2587     }
2588     return err;
2589 }
2590 
2591 ///////////////////////////////////////////////////////////////////////////////
2592 // [zero]
2593 
XZeroAtF(XPARM File_t * f,off_t off,size_t count)2594 enumError XZeroAtF ( XPARM File_t * f, off_t off, size_t count )
2595 {
2596     ASSERT(f);
2597     TRACE(TRACE_RDWR_FORMAT, "#F# ZeroAtF()",
2598 		GetFD(f), GetFP(f), (u64)off, (u64)off+count, count,
2599 		off < f->max_off ? " <" : "" );
2600 
2601     if ( !f->is_reading || !S_ISREG(f->st.st_mode) )
2602 	return XWriteZeroAtF(XCALL f,off,count);
2603 
2604     //----- check file growing
2605 
2606     off_t last_off = off + count;
2607     if ( last_off > f->max_off )
2608     {
2609 	const off_t max_off = f->max_off;
2610 	const enumError err = XSetSizeF( XCALL f, last_off );
2611 	if ( err || off >= max_off )
2612 	    return err;
2613 
2614 	ASSERT( count > last_off - max_off );
2615 	count -= last_off - max_off;
2616 	last_off = max_off;
2617     }
2618 
2619     //----- try to align to blocks
2620 
2621     char buf[0x20000];
2622     const size_t blocksize = f->st.st_blksize < HD_SECTOR_SIZE
2623 				? HD_SECTOR_SIZE
2624 				: f->st.st_blksize > sizeof(buf)
2625 					? sizeof(buf)
2626 					: f->st.st_blksize;
2627 
2628     if ( off/blocksize != (last_off-1)/blocksize )
2629     {
2630 	// align to blocksize
2631 	const size_t count1 = blocksize - off % blocksize;
2632 	ASSERT( count1 < count );
2633 	const enumError err = XZeroAtF(XCALL f,off,count1);
2634 	if (err)
2635 	    return err;
2636 	off   += count1;
2637 	count -= count1;
2638     }
2639 
2640     //----- the main loop
2641 
2642     while ( count > 0 )
2643     {
2644 	//--- read data
2645 
2646 	const size_t rsize = count < sizeof(buf) ? count : sizeof(buf);
2647 	const enumError err = XReadAtF(XCALL f,off,buf,rsize);
2648 	if (err)
2649 	    return err;
2650 
2651 	//--- iterate read data
2652 
2653 	char * ptr = buf;
2654 	char * end = buf + rsize;
2655 
2656 	while ( ptr < end )
2657 	{
2658 	    //--- find begining of non zero data
2659 
2660 	    while ( ptr < end && !*ptr )
2661 		ptr++;
2662 
2663 	    //--- set start block aligned
2664 
2665 	    size_t start = ((ptr-buf)/blocksize)*blocksize;
2666 	    ptr = buf + start + blocksize;
2667 
2668 	    //--- find zero block
2669 
2670 	    while ( ptr < end )
2671 	    {
2672 		char * bl_end = ptr + blocksize;
2673 		if ( bl_end > end )
2674 		    bl_end = end;
2675 		while ( ptr < bl_end && !*ptr )
2676 		    ptr++;
2677 		if ( ptr == bl_end )
2678 		    break;
2679 		ptr = bl_end;
2680 	    }
2681 
2682 	    if ( ptr > end )
2683 		ptr = end;
2684 
2685 	    const enumError err = XWriteZeroAtF(XCALL f, off+start, ptr-buf-start );
2686 	    if (!err)
2687 		return err;
2688 	}
2689 
2690 	off   += rsize;
2691 	count -= rsize;
2692     }
2693     return ERR_OK;
2694 }
2695 
2696 //
2697 ///////////////////////////////////////////////////////////////////////////////
2698 ///////////////////////////////////////////////////////////////////////////////
2699 
WrapperReadSector(void * handle,u32 lba,u32 count,void * iobuf)2700 int WrapperReadSector ( void * handle, u32 lba, u32 count, void * iobuf )
2701 {
2702     ASSERT(handle);
2703     SuperFile_t * sf = (SuperFile_t*)handle;
2704 
2705     TRACE("WrapperReadSector(fd=%d,lba=%x,count=%x) sector-size=%u\n",
2706 		GetFD(&sf->f), lba, count, sf->f.sector_size );
2707 
2708     if (SIGINT_level>1)
2709 	return ERR_INTERRUPT;
2710 
2711     return ReadAtF(
2712 		&sf->f,
2713 		(off_t)lba * sf->f.sector_size,
2714 		iobuf,
2715 		count * sf->f.sector_size );
2716 }
2717 
2718 ///////////////////////////////////////////////////////////////////////////////
2719 
WrapperWriteSector(void * handle,u32 lba,u32 count,void * iobuf)2720 int WrapperWriteSector ( void * handle, u32 lba, u32 count, void * iobuf )
2721 {
2722     ASSERT(handle);
2723     SuperFile_t * sf = (SuperFile_t*)handle;
2724 
2725     TRACE("WBFS: WrapperWriteSector(fd=%d,lba=%x,count=%x) sector-size=%u\n",
2726 		GetFD(&sf->f), lba, count, sf->f.sector_size );
2727 
2728     if (SIGINT_level>1)
2729 	return ERR_INTERRUPT;
2730 
2731     return WriteAtF(
2732 		&sf->f,
2733 		(off_t)lba * sf->f.sector_size,
2734 		iobuf,
2735 		count * sf->f.sector_size );
2736 }
2737 
2738 ///////////////////////////////////////////////////////////////////////////////
2739 
GetFD(const File_t * f)2740 int GetFD ( const File_t * f )
2741 {
2742     return !f ? -1 : f->split_used > 0 ? f->split_f[0]->fd : f->fd;
2743 }
2744 
2745 ///////////////////////////////////////////////////////////////////////////////
2746 
GetFP(const File_t * f)2747 FILE * GetFP ( const File_t * f )
2748 {
2749     return !f ? 0 : f->split_used > 0 ? f->split_f[0]->fp : f->fp;
2750 }
2751 
2752 ///////////////////////////////////////////////////////////////////////////////
2753 
GetFT(const File_t * f)2754 char GetFT ( const File_t * f )
2755 {
2756     if (!f)
2757 	return '%';
2758 
2759     if ( f->split_used > 0 )
2760 	f = f->split_f[0];
2761 
2762     return f->fp ? 'S' : f->fd != -1 ? 'F' : '-';
2763 }
2764 
2765 ///////////////////////////////////////////////////////////////////////////////
2766 
IsOpenF(const File_t * f)2767 bool IsOpenF ( const File_t * f )
2768 {
2769     return f && ( f->split_used > 0 ? f->split_f[0]->fd : f->fd ) != -1;
2770 }
2771 
2772 ///////////////////////////////////////////////////////////////////////////////
2773 
IsSplittedF(const File_t * f)2774 bool IsSplittedF ( const File_t * f )
2775 {
2776     return f && f->split_used > 0;
2777 }
2778 
2779 ///////////////////////////////////////////////////////////////////////////////
2780 
IsDirectory(ccp fname,bool answer_if_empty)2781 bool IsDirectory ( ccp fname, bool answer_if_empty )
2782 {
2783     if ( !fname || !*fname )
2784 	return answer_if_empty;
2785 
2786     if ( *fname == '-' && !fname[1] )
2787 	return false;
2788 
2789     if ( fname[strlen(fname)-1] == '/' )
2790 	return true;
2791 
2792     struct stat st;
2793     return !stat(fname,&st) && S_ISDIR(st.st_mode);
2794 }
2795 
2796 ///////////////////////////////////////////////////////////////////////////////
2797 
GetFileMode(mode_t mode)2798 enumFileMode GetFileMode ( mode_t mode )
2799 {
2800     return S_ISREG(mode) ? FM_PLAIN
2801 	 : S_ISBLK(mode) ? FM_BLKDEV
2802 	 : S_ISCHR(mode) ? FM_CHRDEV
2803 	 : 0;
2804 }
2805 
GetFileModeText(enumFileMode mode,bool longtext,ccp fail_text)2806 ccp GetFileModeText ( enumFileMode mode, bool longtext, ccp fail_text )
2807 {
2808     switch (mode)
2809     {
2810 	case FM_PLAIN:
2811 	    return longtext ? "PLAIN FILE" : "PLAIN";
2812 
2813 	case FM_BLKDEV:
2814 	    return longtext ? "BLOCK DEVICE" : "BLOCK";
2815 
2816 	case FM_CHRDEV:
2817 	    return longtext ? "CHARACTER DEVICE" : "CHAR";
2818 
2819 	default:
2820 	    return fail_text;
2821     }
2822 }
2823 
2824 ///////////////////////////////////////////////////////////////////////////////
2825 
CreatePath(ccp fname)2826 enumError CreatePath ( ccp fname )
2827 {
2828     TRACE("CreatePath(%s)\n",fname);
2829 
2830     char buf[PATH_MAX], *dest = buf;
2831     StringCopyS(buf,sizeof(buf),fname);
2832 
2833     for(;;)
2834     {
2835 	// skip slashes
2836 	while ( *dest == '/' )
2837 	    dest++;
2838 
2839 	// search end of current directory
2840 	while ( *dest && *dest != '/' )
2841 	    dest++;
2842 	if (!*dest)
2843 	    break;
2844 
2845 	*dest = 0;
2846 	if ( mkdir(buf,0777) && errno != EEXIST && !IsDirectory(buf,0) )
2847 	{
2848 	    noTRACE("CREATE-DIR: %s -> err=%d (ENOTDIR=%d)\n",buf,errno,ENOTDIR);
2849 	    if ( errno == ENOTDIR )
2850 	    {
2851 		while ( dest > buf && *dest != '/' )
2852 		    dest--;
2853 		if ( dest > buf )
2854 		    *dest = 0;
2855 	    }
2856 	    return ERROR1( ERR_CANT_CREATE_DIR,
2857 		errno == ENOTDIR
2858 			? "Not a directory: %s\n"
2859 			: "Can't create directory: %s\n", buf );
2860 	}
2861 	TRACE("CREATE-DIR: %s -> OK\n",buf);
2862 	*dest++ = '/';
2863     }
2864     return ERR_OK;
2865 }
2866 
2867 ///////////////////////////////////////////////////////////////////////////////
2868 
GetFileSize(ccp path1,ccp path2,s64 not_found_val,FileAttrib_t * fatt,bool fatt_max)2869 s64 GetFileSize
2870 (
2871     ccp			path1,		// NULL or part 1 of path
2872     ccp			path2,		// NULL or part 2 of path
2873     s64			not_found_val,	// return value if no regular file found
2874     FileAttrib_t	* fatt,		// not NULL: store file attributes
2875     bool		fatt_max	// true: store max values to 'fatt'
2876 )
2877 {
2878     char pathbuf[PATH_MAX];
2879     ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2880     TRACE("GetFileSize(%s,%lld)\n",path,not_found_val);
2881 
2882     struct stat st;
2883     if ( stat(path,&st) || !S_ISREG(st.st_mode) )
2884     {
2885 	if ( fatt && !fatt_max )
2886 	    memset(fatt,0,sizeof(*fatt));
2887 	return not_found_val;
2888     }
2889 
2890     if (fatt)
2891 	CopyFileAttribStat(fatt,&st,fatt_max);
2892 
2893     return st.st_size;
2894 }
2895 
2896 ///////////////////////////////////////////////////////////////////////////////
2897 
LoadFile(ccp path1,ccp path2,size_t skip,void * data,size_t size,bool silent,FileAttrib_t * fatt,bool fatt_max)2898 enumError LoadFile
2899 (
2900     ccp			path1,		// NULL or part #1 of path
2901     ccp			path2,		// NULL or part #2 of path
2902     size_t		skip,		// skip num of bytes before reading
2903     void		* data,		// destination buffer, size = 'size'
2904     size_t		size,		// size to read
2905     bool		silent,		// true: suppress printing of error messages
2906     FileAttrib_t	* fatt,		// not NULL: store file attributes
2907     bool		fatt_max	// true: store max values to 'fatt'
2908 )
2909 {
2910     // [[2do]] error handling
2911 
2912     ASSERT(data);
2913     if ( fatt && !fatt_max )
2914 	memset(fatt,0,sizeof(*fatt));
2915 
2916     if (!size)
2917 	return ERR_OK;
2918 
2919     char pathbuf[PATH_MAX];
2920     ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2921     TRACE("LoadFile(%s,%zu,%zu,%d)\n",path,skip,size,silent);
2922 
2923     FILE * f = fopen(path,"rb");
2924     if (!f)
2925     {
2926 	if (!silent)
2927 	    ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path);
2928 	return ERR_CANT_OPEN;
2929     }
2930 
2931     if (fatt)
2932     {
2933 	struct stat st;
2934 	if (!fstat(fileno(f),&st))
2935 	    CopyFileAttribStat(fatt,&st,fatt_max);
2936     }
2937 
2938     if ( skip > 0 )
2939 	fseek(f,skip,SEEK_SET);
2940 
2941     size_t read_stat = fread(data,1,size,f);
2942     fclose(f);
2943 
2944     if ( read_stat == size )
2945 	return ERR_OK;
2946 
2947     noPRINT("D=%p, s=%zu/%zu: %s\n",data,read_stat,size,path);
2948     if ( read_stat >= 0 && read_stat < size )
2949 	memset((char*)data+read_stat,0,size-read_stat);
2950 
2951     return ERR_WARNING;
2952 }
2953 
2954 ///////////////////////////////////////////////////////////////////////////////
2955 
LoadFileAlloc(ccp path1,ccp path2,size_t skip,u8 ** res_data,size_t * res_size,size_t max_size,bool silent,FileAttrib_t * fatt,bool fatt_max)2956 enumError LoadFileAlloc
2957 (
2958     ccp			path1,		// NULL or part #1 of path
2959     ccp			path2,		// NULL or part #2 of path
2960     size_t		skip,		// skip num of bytes before reading
2961     u8			** res_data,	// result: free existing data, store ptr to alloc data
2962 					// always one more byte is alloced and set to NULL
2963     size_t		*  res_size,	// result: size of 'res_data'
2964     size_t		max_size,	// >0: a file size limit
2965     bool		silent,		// true: suppress printing of error messages
2966     FileAttrib_t	* fatt,		// not NULL: store file attributes
2967     bool		fatt_max	// true: store max values to 'fatt'
2968 )
2969 {
2970     DASSERT(res_data);
2971     DASSERT(res_size);
2972 
2973     //--- clear return data
2974 
2975     if (res_data)
2976 	*res_data = 0;
2977 
2978     if (res_size)
2979 	*res_size = 0;
2980 
2981     if ( fatt && !fatt_max )
2982 	memset(fatt,0,sizeof(*fatt));
2983 
2984 
2985     //--- get file size
2986 
2987     char pathbuf[PATH_MAX];
2988     ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2989 
2990     const s64 size = GetFileSize(path,0,-1,0,0);
2991     if ( size == -1 )
2992     {
2993 	if (!silent)
2994 	    ERROR0(ERR_SYNTAX,"File not found: %s\n",path);
2995 	return ERR_SYNTAX;
2996     }
2997 
2998     if ( max_size && size > max_size )
2999     {
3000 	if (!silent)
3001 	    ERROR0(ERR_INVALID_FILE,"File to large: %s\n",path);
3002 	return ERR_INVALID_FILE;
3003     }
3004 
3005     u8 *data = MALLOC(size+1);
3006     enumError err = LoadFile(path,0,skip,data,size,silent,fatt,fatt_max);
3007     if (err)
3008     {
3009 	FREE(data);
3010 	return err;
3011     }
3012 
3013     if (res_data)
3014     {
3015 	data[size] = 0;
3016 	*res_data = data;
3017     }
3018     else
3019 	FREE(data);
3020 
3021     if (res_size)
3022 	*res_size = size;
3023 
3024     return err;
3025 }
3026 
3027 ///////////////////////////////////////////////////////////////////////////////
3028 
CheckCreateFile(ccp fname,bool detect_stdout,bool overwrite,bool silent,struct stat * st)3029 enumError CheckCreateFile
3030 (
3031     // returns:
3032     //   ERR_WARNING:		source is "-" (stdout) => 'st' is zeroed
3033     //   ERR_ALREADY_EXISTS:	file already exists
3034     //   ERR_WRONG_FILE_TYPE:	file exists and file type is wrong
3035     //   ERR_OK:		file not exist or can be overwritten
3036 
3037     ccp		fname,		// filename to open
3038     bool	detect_stdout,	// true: detect "-" as stdout
3039     bool	overwrite,	// true: overwriting is allowed
3040     bool	silent,		// true: suppress error messages
3041     struct stat	*st		// not NULL: store file status here
3042 )
3043 {
3044 
3045     if ( detect_stdout && fname[0] == '-' && !fname[1] )
3046     {
3047 	if (st)
3048 	    memset(st,0,sizeof(*st));
3049 	return ERR_WARNING;
3050     }
3051 
3052     struct stat local_st;
3053     if (!st)
3054 	st = &local_st;
3055 
3056     if (!stat(fname,st))
3057     {
3058 	if ( S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) )
3059 	{
3060 	    if (!silent)
3061 		ERROR0( ERR_ALREADY_EXISTS,
3062 			"Can't write to %s device: %s\n",
3063 			S_ISBLK(st->st_mode) ? "block" : "character", fname );
3064 	    return ERR_WRONG_FILE_TYPE;
3065 	}
3066 
3067 	if (!S_ISREG(st->st_mode))
3068 	{
3069 	    if (!silent)
3070 		ERROR0( ERR_WRONG_FILE_TYPE,
3071 		    "Not a plain file: %s\n", fname );
3072 	    return ERR_WRONG_FILE_TYPE;
3073 	}
3074 
3075 	if (!overwrite)
3076 	{
3077 	    if (!silent)
3078 		ERROR0( ERR_ALREADY_EXISTS,
3079 		    "File already exists: %s\n", fname );
3080 	    return ERR_ALREADY_EXISTS;
3081 	}
3082     }
3083     return ERR_OK;
3084 }
3085 
3086 ///////////////////////////////////////////////////////////////////////////////
3087 
SaveFile(ccp path1,ccp path2,bool overwrite,bool create_dir,const void * data,size_t size,bool silent)3088 enumError SaveFile
3089 (
3090     ccp			path1,		// NULL or part #1 of path
3091     ccp			path2,		// NULL or part #2 of path
3092     bool		overwrite,	// true: overwrite existing files
3093     bool		create_dir,	// true: create path automatically
3094     const void		* data,		// pointer to data
3095     size_t		size,		// size of 'data'
3096     bool		silent		// true: suppress error messages
3097 )
3098 {
3099     ASSERT(data);
3100 
3101     char pathbuf[PATH_MAX];
3102     ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
3103     TRACE("SaveFile(%s,%zx,%d)\n",path,size,silent);
3104 
3105     enumError err = CheckCreateFile(path,false,overwrite,silent,0);
3106     if (err)
3107 	return err;
3108 
3109     FILE * f = fopen(path,"wb");
3110     if (!f)
3111     {
3112 	if (create_dir)
3113 	{
3114 	    CreatePath(path);
3115 	    f = fopen(path,"wb");
3116 	}
3117 
3118 	if (!f)
3119 	{
3120 	    if (!silent)
3121 		ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",path);
3122 	    return ERR_CANT_CREATE;
3123 	}
3124     }
3125 
3126     size_t stat = fwrite(data,1,size,f);
3127     if ( stat != size )
3128     {
3129 	err = ERR_WRITE_FAILED;
3130 	if (!silent)
3131 	    ERROR1(err,"Write to file failed: %s\n",path);
3132     }
3133     fclose(f);
3134 
3135     return err;
3136 }
3137 
3138 ///////////////////////////////////////////////////////////////////////////////
3139 ///////////////////////////////////////////////////////////////////////////////
3140 
ClearFileID(File_t * f)3141 void ClearFileID
3142 (
3143     File_t		* f
3144 )
3145 {
3146     DASSERT(f);
3147     memset( f->id6_src,  0, sizeof(f->id6_src) );
3148     memset( f->id6_dest, 0, sizeof(f->id6_dest) );
3149 }
3150 
3151 ///////////////////////////////////////////////////////////////////////////////
3152 
SetFileID(File_t * f,const void * new_id,int id_length)3153 void SetFileID
3154 (
3155     File_t		* f,
3156     const void		* new_id,
3157     int			id_length
3158 )
3159 {
3160     DASSERT(f);
3161     DASSERT(id_length>=0);
3162 
3163     memset( f->id6_src, 0, sizeof(f->id6_src) );
3164     memcpy( f->id6_src, new_id, id_length < 6 ? id_length : 6 );
3165     memcpy( f->id6_dest, f->id6_src, sizeof(f->id6_dest) );
3166     noPRINT("$$$ SetFileID: |%s|%s|\n",f->id6_src,f->id6_dest);
3167 }
3168 
3169 ///////////////////////////////////////////////////////////////////////////////
3170 
SetPatchFileID(File_t * f,const void * new_id,int id_length)3171 bool SetPatchFileID
3172 (
3173     File_t		* f,
3174     const void		* new_id,
3175     int			id_length
3176 )
3177 {
3178     DASSERT(f);
3179     DASSERT(id_length>=0);
3180 
3181     memset( f->id6_src, 0, sizeof(f->id6_src) );
3182     memcpy( f->id6_src, new_id, id_length < 6 ? id_length : 6 );
3183     const bool stat = CopyPatchDiscId( f->id6_dest, f->id6_src );
3184     noPRINT("$$$ SetPatchFileID: |%s|%s| stat=%d\n",f->id6_src,f->id6_dest,stat);
3185     return stat;
3186 }
3187 
3188 //
3189 ///////////////////////////////////////////////////////////////////////////////
3190 ///////////////                  FileAttrib_t                   ///////////////
3191 ///////////////////////////////////////////////////////////////////////////////
3192 
NormalizeFileAttrib(FileAttrib_t * fa)3193 FileAttrib_t * NormalizeFileAttrib
3194 (
3195     FileAttrib_t	* fa		// valid attribute
3196 )
3197 {
3198     DASSERT(fa);
3199 
3200     if ( fa->size < 0 )
3201 	fa->size = 0;
3202 
3203     time_t mtime = fa->mtime;
3204     if (!mtime)
3205     {
3206 	mtime = fa->itime;
3207 	if (!mtime)
3208 	{
3209 	    mtime = fa->ctime;
3210 	    if (!mtime)
3211 		mtime = fa->atime;
3212 	}
3213     }
3214     fa->mtime = mtime;
3215 
3216     if (!fa->itime)
3217 	fa->itime = fa->ctime > mtime ? fa->ctime : mtime;
3218 
3219     if (!fa->ctime)
3220 	fa->ctime = fa->itime > mtime ? fa->itime : mtime;
3221 
3222     if (!fa->atime)
3223 	fa->atime = fa->itime > fa->ctime ? fa->itime : fa->ctime;
3224 
3225     return fa;
3226 }
3227 
3228 //-----------------------------------------------------------------------------
3229 
MaxFileAttrib(FileAttrib_t * dest,const FileAttrib_t * src)3230 FileAttrib_t * MaxFileAttrib
3231 (
3232     FileAttrib_t	* dest,		// source and destination attribute
3233     const FileAttrib_t	* src		// NULL or second source attribute
3234 )
3235 {
3236     DASSERT(dest);
3237     if (src)
3238     {
3239 	if ( dest->size < src->size )
3240 	     dest->size = src->size;
3241 	if ( dest->itime < src->itime )
3242 	     dest->itime = src->itime;
3243 	if ( dest->mtime < src->mtime )
3244 	     dest->mtime = src->mtime;
3245 	if ( dest->ctime < src->ctime )
3246 	     dest->ctime = src->ctime;
3247 	if ( dest->atime < src->atime )
3248 	     dest->atime = src->atime;
3249     }
3250     return dest;
3251 }
3252 
3253 //-----------------------------------------------------------------------------
3254 
CopyFileAttrib(FileAttrib_t * dest,const FileAttrib_t * src)3255 FileAttrib_t * CopyFileAttrib
3256 (
3257     FileAttrib_t	* dest,		// valid destination attribute
3258     const FileAttrib_t	* src		// valid source attribute
3259 )
3260 {
3261     DASSERT(src);
3262     DASSERT(dest);
3263 
3264     memcpy(dest,src,sizeof(*dest));
3265     return NormalizeFileAttrib(dest);
3266 }
3267 
3268 //-----------------------------------------------------------------------------
3269 
CopyFileAttribStat(FileAttrib_t * dest,const struct stat * src,bool maximize)3270 FileAttrib_t * CopyFileAttribStat
3271 (
3272     FileAttrib_t	* dest,		// valid destination attribute
3273     const struct stat	* src,		// NULL or source
3274     bool		maximize	// true store max values to 'dest'
3275 )
3276 {
3277     DASSERT(dest);
3278 
3279     if (src)
3280     {
3281 	FileAttrib_t temp_fatt;
3282 	FileAttrib_t * fatt = maximize ? &temp_fatt : dest;
3283 
3284 	memset(fatt,0,sizeof(*fatt));
3285 	fatt->size = src->st_size;
3286 
3287 	if ( S_ISREG(src->st_mode) )
3288 	{
3289 	    fatt->mtime = src->st_mtime;
3290 	    fatt->ctime = src->st_ctime;
3291 	    fatt->atime = src->st_atime;
3292 	}
3293 	NormalizeFileAttrib(fatt);
3294 
3295 	if (maximize)
3296 	    MaxFileAttrib(dest,fatt);
3297     }
3298     else if (!maximize)
3299 	memset(dest,0,sizeof(*dest));
3300 
3301     return dest;
3302 }
3303 
3304 //-----------------------------------------------------------------------------
3305 
CopyFileAttribInode(FileAttrib_t * dest,const struct wbfs_inode_info_t * src,off_t size)3306 FileAttrib_t * CopyFileAttribInode
3307 	( FileAttrib_t * dest, const struct wbfs_inode_info_t * src, off_t size )
3308 {
3309     ASSERT(src);
3310     ASSERT(dest);
3311 
3312     dest->size  = size;
3313     if (wbfs_is_inode_info_valid(0,src))
3314     {
3315 	dest->itime = ntoh64(src->itime);
3316 	dest->mtime = ntoh64(src->mtime);
3317 	dest->ctime = ntoh64(src->ctime);
3318 	dest->atime = ntoh64(src->atime);
3319     }
3320 
3321     return NormalizeFileAttrib(dest);
3322 }
3323 
3324 //-----------------------------------------------------------------------------
3325 
CopyFileAttribDiscInfo(FileAttrib_t * dest,const struct WDiscInfo_t * src)3326 FileAttrib_t * CopyFileAttribDiscInfo
3327 	( FileAttrib_t * dest, const struct WDiscInfo_t * src )
3328 {
3329     ASSERT(src);
3330     ASSERT(dest);
3331 
3332     return CopyFileAttribInode(dest,&src->dhead.iinfo,src->size);
3333 }
3334 
3335 //
3336 ///////////////////////////////////////////////////////////////////////////////
3337 ///////////////			split file support		///////////////
3338 ///////////////////////////////////////////////////////////////////////////////
3339 
CalcSplitFilename(char * buf,size_t buf_size,ccp path,enumOFT oft)3340 int CalcSplitFilename ( char * buf, size_t buf_size, ccp path, enumOFT oft )
3341 {
3342     const int needed_space = oft == OFT_WBFS ? 6 : 7;
3343     const int max_path_len = PATH_MAX - needed_space;
3344 
3345     if (!path)
3346 	path = "";
3347     TRACE("CalcSplitFilename(%s,%d)\n",path,oft);
3348 
3349     size_t plen = strlen(path);
3350     if ( plen > 0 && oft == OFT_WBFS )
3351 	plen--;
3352     if ( plen > max_path_len )
3353 	plen = max_path_len;
3354     ccp path_end = path + plen;
3355 
3356     char * dest = buf;
3357     if ( buf_size > needed_space )
3358     {
3359 	char * end = dest + buf_size - needed_space;
3360 	while ( dest < end && path < path_end )
3361 	{
3362 	    if ( *path == '%' )
3363 	    {
3364 		*dest++ = '%';
3365 		*dest++ = '%';
3366 		path++;
3367 	    }
3368 	    else
3369 		*dest++	= *path++;
3370 	}
3371 
3372 	if ( oft != OFT_WBFS )
3373 	    *dest++ = '.';
3374 	*dest++ = '%';
3375 	*dest++ = '0';
3376 	*dest++ = '1';
3377 	*dest++ = 'u';
3378     }
3379     *dest = 0;
3380     return dest-buf;
3381 }
3382 
3383 //-----------------------------------------------------------------------------
3384 
AllocSplitFilename(ccp path,enumOFT oft)3385 char * AllocSplitFilename ( ccp path, enumOFT oft )
3386 {
3387     char buf[2*PATH_MAX];
3388     CalcSplitFilename(buf,sizeof(buf),path,oft);
3389     return STRDUP(buf);
3390 }
3391 
3392 //
3393 ///////////////////////////////////////////////////////////////////////////////
3394 ///////////////			GetFileSystemMap()		///////////////
3395 ///////////////////////////////////////////////////////////////////////////////
3396 #if HAVE_FIEMAP
3397 ///////////////////////////////////////////////////////////////////////////////
3398 
GetFileMapHelper_FIEMAP(FileMap_t * fm,File_t * file,struct fiemap * fmap,uint n_elem)3399  static enumError GetFileMapHelper_FIEMAP
3400  (
3401     FileMap_t		* fm,		// file map
3402     File_t		* file,		// file to analyze
3403     struct fiemap	* fmap,		// valid fiemap structure
3404     uint		n_elem		// number of 'fmap->fm_extents' elements
3405  )
3406  {
3407     DASSERT(fm);
3408     DASSERT(file);
3409     DASSERT(fmap);
3410     DASSERT(n_elem>0);
3411 
3412     memset(fmap,0,sizeof(fmap));
3413 
3414     u64 last_off = 0;
3415     while ( last_off < file->st.st_size )
3416     {
3417 	fmap->fm_start	  = last_off;
3418 	fmap->fm_length	  = file->st.st_size - last_off;
3419 	fmap->fm_flags	  = FIEMAP_FLAG_SYNC;
3420 	fmap->fm_extent_count = n_elem;
3421 	int stat = ioctl(file->fd,FS_IOC_FIEMAP,fmap);
3422 	if ( stat < 0 )
3423 	    return ERR_WARNING; // delayed error message
3424 
3425 	PRINT("STAT=%d, N=%d\n",stat,fmap->fm_mapped_extents);
3426 	if (!fmap->fm_mapped_extents)
3427 	    break;
3428 
3429 	const struct fiemap_extent *fe = fmap->fm_extents;
3430 	const struct fiemap_extent *fe_end = fe + fmap->fm_mapped_extents;
3431 	for ( ; fe < fe_end; fe++ )
3432 	{
3433 	    AppendFileMap( fm, fe->fe_logical + file->split_off,
3434 				fe->fe_physical, fe->fe_length);
3435 	    noPRINT("   -> %9llx %9llx %9llx\n",
3436 			fe->fe_logical, fe->fe_physical, fe->fe_length );
3437 	}
3438 	if ( fe[-1].fe_flags & FIEMAP_EXTENT_LAST )
3439 	    break;
3440 	last_off = fe[-1].fe_logical + fe[-1].fe_length;
3441     }
3442 
3443     return ERR_OK;
3444  }
3445 
3446  //////////////////////////////////////////////////////////////////////////////
3447 
GetFileSystemMap_FIEMAP(FileMap_t * fm,File_t * file)3448  static enumError GetFileSystemMap_FIEMAP
3449  (
3450     FileMap_t		* fm,		// file map
3451     File_t		* file		// file to analyze
3452  )
3453  {
3454     DASSERT(fm);
3455     DASSERT(file);
3456 
3457     struct fiemap *fmap;
3458     const uint MAX_ELEM = 1024;
3459     const uint ALLOC_SIZE = sizeof(*fmap) + sizeof(*fmap->fm_extents) * MAX_ELEM;
3460     PRINT("ALLOC FIEMAP: %zu + %zu * %u = %u\n",
3461 		sizeof(*fmap), sizeof(*fmap->fm_extents), MAX_ELEM, ALLOC_SIZE );
3462     fmap = MALLOC(ALLOC_SIZE);
3463 
3464     enumError err = ERR_OK;
3465     if (file->split_f)
3466     {
3467 	File_t **end, **ptr = file->split_f;
3468 	for ( end = ptr + file->split_used; err == ERR_OK && ptr < end; ptr++ )
3469 	    err = GetFileMapHelper_FIEMAP(fm,*ptr,fmap,MAX_ELEM);
3470     }
3471     else
3472 	err = GetFileMapHelper_FIEMAP(fm,file,fmap,MAX_ELEM);
3473 
3474     FREE(fmap);
3475     return err;
3476  }
3477 
3478 ///////////////////////////////////////////////////////////////////////////////
3479 #endif // HAVE_FIEMAP
3480 ///////////////////////////////////////////////////////////////////////////////
3481 #if HAVE_FIBMAP
3482 ///////////////////////////////////////////////////////////////////////////////
3483 
GetFileSystemMap_FIBMAP(FileMap_t * fm,File_t * file)3484  static enumError GetFileSystemMap_FIBMAP
3485  (
3486     FileMap_t		* fm,		// file map
3487     File_t		* file		// file to analyze
3488  )
3489  {
3490     DASSERT(fm);
3491     DASSERT(file);
3492 
3493     if (file->split_f)
3494     {
3495 	File_t **end, **ptr = file->split_f;
3496 	for ( end = ptr + file->split_used; ptr < end; ptr++ )
3497 	{
3498 	    enumError err = GetFileSystemMap_FIBMAP(fm,*ptr);
3499 	    if (err)
3500 		return err;
3501 	}
3502 	return ERR_OK;
3503     }
3504 
3505     int blocksize;
3506     if ( ioctl(file->fd,FIGETBSZ,&blocksize) < 0 )
3507 	return ERROR1(ERR_READ_FAILED,
3508 		"Can't read block size (FIGETBSZ): %s\n",file->fname);
3509 
3510     uint block, n_blocks = ( file->st.st_size + blocksize - 1 ) / blocksize;
3511     PRINT("FIBMAP: %u blocks * %u bytes\n",n_blocks,blocksize);
3512 
3513     for ( block = 0; block < n_blocks; block++ )
3514     {
3515 	uint param = block;
3516 	if ( ioctl(file->fd,FIBMAP,&param) < 0 )
3517 	    return ERR_WARNING; // delayed error message
3518 
3519 	AppendFileMap( fm, block * (u64)blocksize + file->split_off,
3520 				param * (u64)blocksize, blocksize );
3521     }
3522 
3523     return ERR_OK;
3524  }
3525 
3526 ///////////////////////////////////////////////////////////////////////////////
3527 #endif // HAVE_FIBMAP
3528 ///////////////////////////////////////////////////////////////////////////////
3529 
HaveFileSystemMapSupport()3530 bool HaveFileSystemMapSupport()
3531 {
3532  #if HAVE_FIEMAP || HAVE_FIBMAP || defined(__CYGWIN__) && defined(TEST)
3533     return true;
3534  #else
3535     return false;
3536  #endif
3537 }
3538 
3539 ///////////////////////////////////////////////////////////////////////////////
3540 
GetFileSystemMap(FileMap_t * fm,bool init_fm,File_t * file)3541 enumError GetFileSystemMap
3542 (
3543     FileMap_t		* fm,		// file map
3544     bool		init_fm,	// true: initialize 'fm', false: reset 'fm'
3545     File_t		* file		// file to analyze
3546 )
3547 {
3548     DASSERT(fm);
3549     DASSERT(file);
3550     if (init_fm)
3551 	InitializeFileMap(fm);
3552     else
3553 	ResetFileMap(fm);
3554 
3555  #if HAVE_FIEMAP || HAVE_FIBMAP
3556 
3557     enumError stat = ERR_JOB_IGNORED;
3558     if (S_ISREG(file->st.st_mode))
3559     {
3560      #if HAVE_FIEMAP
3561 
3562 	// first, we try FIEMAP
3563 	stat = GetFileSystemMap_FIEMAP(fm,file);
3564 	if (!stat)
3565 	    return ERR_OK;
3566      #endif
3567 
3568      #if HAVE_FIBMAP
3569 
3570 	// if failed, we try FIBMAP
3571 	stat = GetFileSystemMap_FIBMAP(fm,file);
3572 	if (!stat)
3573 	    return ERR_OK;
3574      #endif
3575 
3576 	if ( stat == ERR_WARNING )
3577 	{
3578 	 #if HAVE_FIEMAP && HAVE_FIBMAP
3579 	    return ERROR1(ERR_READ_FAILED,
3580 		"Can't read file mapping (FIEMAP+FIBMAP failed): %s\n",file->fname);
3581 	 #elif HAVE_FIEMAP
3582 	    return ERROR1(ERR_READ_FAILED,
3583 		"Can't read file mapping (FIEMAP failed): %s\n",file->fname);
3584 	 #elif HAVE_FIBMAP
3585 	    return ERROR1(ERR_READ_FAILED,
3586 		"Can't read file mapping (FIBMAP failed): %s\n",file->fname);
3587 	 #endif
3588 	}
3589     }
3590     return stat;
3591 
3592  #elif defined(__CYGWIN__)
3593 
3594     enumError err = ERR_OK;
3595     if (file->split_f)
3596     {
3597 	File_t **end, **ptr = file->split_f;
3598 	for ( end = ptr + file->split_used; err == ERR_OK && ptr < end; ptr++ )
3599 	    if (GetWinFileMap(fm,(*ptr)->fd,(*ptr)->split_off,(*ptr)->st.st_size))
3600 		err = ERR_READ_FAILED;
3601     }
3602     else
3603 	if (GetWinFileMap(fm,file->fd,file->split_off,file->st.st_size))
3604 	    err = ERR_READ_FAILED;
3605 
3606     return err
3607 	? ERROR0(err,"Can't read file mapping: %s\n",file->fname)
3608 	: ERR_OK;
3609 
3610  #else
3611     return ERR_JOB_IGNORED;
3612  #endif
3613 }
3614 
3615 //
3616 ///////////////////////////////////////////////////////////////////////////////
3617 ///////////////			cygwin support			///////////////
3618 ///////////////////////////////////////////////////////////////////////////////
3619 
3620 #ifdef __CYGWIN__
3621 
IsWindowsDriveSpec(ccp src)3622  int IsWindowsDriveSpec ( ccp src )
3623  {
3624     if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' )
3625 	&& src[1] == ':' )
3626     {
3627 	if (!src[2])
3628 	    return 2;
3629 
3630 	if ( src[2] == '/' || src[2] == '\\' )
3631 	    return 3;
3632     }
3633     return 0;
3634  }
3635 
3636 #endif // __CYGWIN__
3637 
3638 ///////////////////////////////////////////////////////////////////////////////
3639 
3640 #ifdef __CYGWIN__
3641 
NormalizeFilenameCygwin(char * buf,size_t bufsize,ccp src)3642  int NormalizeFilenameCygwin ( char * buf, size_t bufsize, ccp src )
3643  {
3644     static char prefix[] = "/cygdrive/";
3645 
3646     if ( bufsize < sizeof(prefix) + 5 )
3647     {
3648 	*buf = 0;
3649 	return 0;
3650     }
3651 
3652     char * end = buf + bufsize - 1;
3653     char * dest = buf;
3654 
3655     if (   ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' )
3656 	&& src[1] == ':'
3657 	&& ( src[2] == 0 || src[2] == '/' || src[2] == '\\' ))
3658     {
3659 	memcpy(buf,prefix,sizeof(prefix));
3660 	dest = buf + sizeof(prefix)-1;
3661 	*dest++ = tolower((int)*src); // cygwin needs the '(int)'
3662 	*dest = 0;
3663 	if (IsDirectory(buf,false))
3664 	{
3665 	    *dest++ = '/';
3666 	    src += 2;
3667 	    if (*src)
3668 		src++;
3669 	}
3670 	else
3671 	    dest = buf;
3672     }
3673     ASSERT( dest < buf + bufsize );
3674 
3675     while ( dest < end && *src )
3676 	if ( *src == '\\' )
3677 	{
3678 	    *dest++ = '/';
3679 	    src++;
3680 	}
3681 	else
3682 	    *dest++ = *src++;
3683 
3684     *dest = 0;
3685     ASSERT( dest < buf + bufsize );
3686     return dest - buf;
3687  }
3688 
3689 #endif // __CYGWIN__
3690 
3691 ///////////////////////////////////////////////////////////////////////////////
3692 
3693 #ifdef __CYGWIN__
3694 
AllocNormalizedFilenameCygwin(ccp source)3695  char * AllocNormalizedFilenameCygwin ( ccp source )
3696  {
3697     char buf[PATH_MAX];
3698     const int len = NormalizeFilenameCygwin(buf,sizeof(buf),source);
3699     char * result = MALLOC(len+1);
3700     memcpy(result,buf,len+1);
3701     ASSERT(buf[len]==0);
3702     return result;
3703  }
3704 
3705 #endif // __CYGWIN__
3706 
3707 ///////////////////////////////////////////////////////////////////////////////
3708 
SetDest(ccp dest,bool mkdir)3709 void SetDest ( ccp dest, bool mkdir )
3710 {
3711  #ifdef __CYGWIN__
3712     opt_dest = IsWindowsDriveSpec(dest)
3713 		? AllocNormalizedFilenameCygwin(dest)
3714 		: dest;
3715  #else
3716     opt_dest = dest;
3717  #endif
3718     opt_mkdir = mkdir;
3719 }
3720 
3721 //
3722 ///////////////////////////////////////////////////////////////////////////////
3723 ///////////////			cert support			///////////////
3724 ///////////////////////////////////////////////////////////////////////////////
3725 
AddCertFile(ccp fname,int unused)3726 int AddCertFile ( ccp fname, int unused )
3727 {
3728     SuperFile_t sf;
3729     InitializeSF(&sf);
3730     enumError err = OpenSF(&sf,fname,true,false);
3731     if (!err)
3732     {
3733 	if ( sf.f.ftype & FT_A_ISO )
3734 	{
3735 	    wd_disc_t * disc = OpenDiscSF(&sf,false,false);
3736 	    if (disc)
3737 	    {
3738 		int ip;
3739 		for ( ip = 0; ip < disc->n_part; ip++ )
3740 		{
3741 		    wd_part_t * part = disc->part + ip;
3742 		    if ( part->is_enabled
3743 			&& !wd_load_part(part,true,false,true)
3744 			&& part->cert )
3745 		    {
3746 			cert_append_data(&global_cert,part->cert,part->ph.cert_size,true);
3747 		    }
3748 		}
3749 	    }
3750 	}
3751 // [[2do]] [[ft-id]]
3752 	else if ( sf.f.ftype & (FT_ID_CERT_BIN|FT_ID_TIK_BIN|FT_ID_TMD_BIN) )
3753 	{
3754 	    const size_t load_size = sf.file_size < sizeof(iobuf)
3755 				   ? sf.file_size : sizeof(iobuf);
3756 	    err = ReadSF(&sf,0,iobuf,load_size);
3757 	    if (!err)
3758 	    {
3759 		size_t skip = 0;
3760 		if ( sf.f.ftype & FT_ID_TIK_BIN )
3761 		    skip = sizeof(wd_ticket_t);
3762 		else if ( sf.f.ftype & FT_ID_TMD_BIN )
3763 		{
3764 		    wd_tmd_t * tmd = (wd_tmd_t*)iobuf;
3765 		    skip = sizeof(wd_tmd_t)
3766 			 + ntohs(tmd->n_content) * sizeof(wd_tmd_content_t);
3767 		}
3768 
3769 		if ( skip < load_size )
3770 		    cert_append_data(&global_cert,iobuf+skip,load_size-skip,true);
3771 	    }
3772 	}
3773     }
3774 
3775     ResetSF(&sf,0);
3776     return err;
3777 }
3778 
3779 //
3780 ///////////////////////////////////////////////////////////////////////////////
3781 ///////////////			    END				///////////////
3782 ///////////////////////////////////////////////////////////////////////////////
3783 
3784